@push.rocks/smartacme 8.0.0 → 9.0.0

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 (50) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/acme/acme.classes.account.d.ts +20 -0
  3. package/dist_ts/acme/acme.classes.account.js +37 -0
  4. package/dist_ts/acme/acme.classes.challenge.d.ts +22 -0
  5. package/dist_ts/acme/acme.classes.challenge.js +36 -0
  6. package/dist_ts/acme/acme.classes.client.d.ts +68 -0
  7. package/dist_ts/acme/acme.classes.client.js +71 -0
  8. package/dist_ts/acme/acme.classes.crypto.d.ts +50 -0
  9. package/dist_ts/acme/acme.classes.crypto.js +180 -0
  10. package/dist_ts/acme/acme.classes.directory.d.ts +13 -0
  11. package/dist_ts/acme/acme.classes.directory.js +14 -0
  12. package/dist_ts/acme/acme.classes.error.d.ts +44 -0
  13. package/dist_ts/acme/acme.classes.error.js +41 -0
  14. package/dist_ts/acme/acme.classes.http-client.d.ts +36 -0
  15. package/dist_ts/acme/acme.classes.http-client.js +201 -0
  16. package/dist_ts/acme/acme.classes.order.d.ts +40 -0
  17. package/dist_ts/acme/acme.classes.order.js +100 -0
  18. package/dist_ts/acme/acme.interfaces.d.ts +64 -0
  19. package/dist_ts/acme/acme.interfaces.js +5 -0
  20. package/dist_ts/acme/index.d.ts +5 -0
  21. package/dist_ts/acme/index.js +5 -0
  22. package/dist_ts/certmanagers/mongo.js +1 -2
  23. package/dist_ts/plugins.d.ts +2 -7
  24. package/dist_ts/plugins.js +5 -11
  25. package/dist_ts/smartacme.classes.smartacme.d.ts +3 -1
  26. package/dist_ts/smartacme.classes.smartacme.js +40 -13
  27. package/npmextra.json +12 -6
  28. package/package.json +23 -24
  29. package/readme.hints.md +27 -3
  30. package/readme.md +201 -263
  31. package/ts/00_commitinfo_data.ts +1 -1
  32. package/ts/acme/acme.classes.account.ts +45 -0
  33. package/ts/acme/acme.classes.challenge.ts +45 -0
  34. package/ts/acme/acme.classes.client.ts +99 -0
  35. package/ts/acme/acme.classes.crypto.ts +220 -0
  36. package/ts/acme/acme.classes.directory.ts +13 -0
  37. package/ts/acme/acme.classes.error.ts +55 -0
  38. package/ts/acme/acme.classes.http-client.ts +236 -0
  39. package/ts/acme/acme.classes.order.ts +125 -0
  40. package/ts/acme/acme.interfaces.ts +74 -0
  41. package/ts/acme/index.ts +16 -0
  42. package/ts/certmanagers/mongo.ts +0 -1
  43. package/ts/plugins.ts +3 -14
  44. package/ts/smartacme.classes.smartacme.ts +41 -16
  45. package/dist_ts/certmanagers.d.ts +0 -42
  46. package/dist_ts/certmanagers.js +0 -86
  47. package/dist_ts/smartacme.classes.certmanager.d.ts +0 -43
  48. package/dist_ts/smartacme.classes.certmanager.js +0 -92
  49. package/dist_ts/smartacme.plugins.d.ts +0 -21
  50. package/dist_ts/smartacme.plugins.js +0 -28
@@ -0,0 +1,125 @@
1
+ import { AcmeCrypto } from './acme.classes.crypto.js';
2
+ import { AcmeError } from './acme.classes.error.js';
3
+ import type { AcmeHttpClient } from './acme.classes.http-client.js';
4
+ import type {
5
+ IAcmeAuthorization,
6
+ IAcmeIdentifier,
7
+ IAcmeOrder,
8
+ } from './acme.interfaces.js';
9
+
10
+ /**
11
+ * ACME order lifecycle management.
12
+ * Handles order creation, authorization retrieval, finalization, and certificate download.
13
+ */
14
+ export class AcmeOrderManager {
15
+ private httpClient: AcmeHttpClient;
16
+
17
+ constructor(httpClient: AcmeHttpClient) {
18
+ this.httpClient = httpClient;
19
+ }
20
+
21
+ /**
22
+ * Create a new ACME order for the given identifiers
23
+ */
24
+ async create(opts: { identifiers: IAcmeIdentifier[] }): Promise<IAcmeOrder> {
25
+ const dir = await this.httpClient.getDirectory();
26
+ const response = await this.httpClient.signedRequest(dir.newOrder, {
27
+ identifiers: opts.identifiers,
28
+ });
29
+
30
+ const order = response.data as IAcmeOrder;
31
+ // Capture order URL from Location header
32
+ order.url = response.headers['location'] || '';
33
+ return order;
34
+ }
35
+
36
+ /**
37
+ * Retrieve all authorizations for an order (POST-as-GET each authorization URL)
38
+ */
39
+ async getAuthorizations(order: IAcmeOrder): Promise<IAcmeAuthorization[]> {
40
+ const authorizations: IAcmeAuthorization[] = [];
41
+ for (const authzUrl of order.authorizations) {
42
+ const response = await this.httpClient.signedRequest(authzUrl, null);
43
+ authorizations.push(response.data as IAcmeAuthorization);
44
+ }
45
+ return authorizations;
46
+ }
47
+
48
+ /**
49
+ * Finalize an order by submitting the CSR.
50
+ * Waits for the order to reach 'valid' status.
51
+ * Mutates the order object with updated status and certificate URL.
52
+ */
53
+ async finalize(order: IAcmeOrder, csrPem: string): Promise<void> {
54
+ // Convert PEM CSR to base64url DER for ACME
55
+ const csrDer = AcmeCrypto.pemToBuffer(csrPem);
56
+ const csrB64url = csrDer.toString('base64url');
57
+
58
+ const response = await this.httpClient.signedRequest(order.finalize, { csr: csrB64url });
59
+
60
+ // Update order with response data
61
+ const updatedOrder = response.data;
62
+ order.status = updatedOrder.status;
63
+ if (updatedOrder.certificate) {
64
+ order.certificate = updatedOrder.certificate;
65
+ }
66
+
67
+ // If not yet valid, poll until it is
68
+ if (order.status !== 'valid' && order.url) {
69
+ const finalOrder = await this.waitForValidStatus({ url: order.url });
70
+ order.status = finalOrder.status;
71
+ order.certificate = finalOrder.certificate;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Download the certificate chain (PEM) from the order's certificate URL
77
+ */
78
+ async getCertificate(order: IAcmeOrder): Promise<string> {
79
+ if (!order.certificate) {
80
+ throw new Error('Order does not have a certificate URL - finalize first');
81
+ }
82
+ const response = await this.httpClient.signedRequest(order.certificate, null);
83
+ // Certificate chain is returned as PEM text
84
+ return typeof response.data === 'string' ? response.data : response.data.toString();
85
+ }
86
+
87
+ /**
88
+ * Poll an ACME resource (order or challenge) until it reaches 'valid' or 'ready' status.
89
+ * Uses exponential backoff with Retry-After header support.
90
+ */
91
+ async waitForValidStatus(
92
+ item: { url: string },
93
+ opts?: { maxAttempts?: number; initialDelayMs?: number },
94
+ ): Promise<any> {
95
+ const maxAttempts = opts?.maxAttempts ?? 30;
96
+ const initialDelay = opts?.initialDelayMs ?? 1000;
97
+
98
+ for (let i = 0; i < maxAttempts; i++) {
99
+ const response = await this.httpClient.signedRequest(item.url, null);
100
+ const body = response.data;
101
+
102
+ if (body.status === 'valid' || body.status === 'ready') {
103
+ return body;
104
+ }
105
+ if (body.status === 'invalid') {
106
+ const challengeError = body.challenges?.find((c: any) => c.error)?.error;
107
+ throw new AcmeError({
108
+ status: 0,
109
+ type: challengeError?.type || 'urn:ietf:params:acme:error:rejectedIdentifier',
110
+ detail: challengeError?.detail || JSON.stringify(body),
111
+ subproblems: challengeError?.subproblems,
112
+ url: item.url,
113
+ });
114
+ }
115
+
116
+ // Respect Retry-After header, otherwise exponential backoff
117
+ const retryAfter = parseInt(response.headers['retry-after'] || '0', 10);
118
+ const delay =
119
+ retryAfter > 0 ? retryAfter * 1000 : Math.min(initialDelay * Math.pow(2, i), 30000);
120
+ await new Promise((resolve) => setTimeout(resolve, delay));
121
+ }
122
+
123
+ throw new Error(`Timeout waiting for valid status after ${maxAttempts} attempts`);
124
+ }
125
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * ACME Protocol interfaces per RFC 8555
3
+ */
4
+
5
+ export interface IAcmeDirectory {
6
+ newNonce: string;
7
+ newAccount: string;
8
+ newOrder: string;
9
+ newAuthz?: string;
10
+ revokeCert?: string;
11
+ keyChange?: string;
12
+ meta?: IAcmeDirectoryMeta;
13
+ }
14
+
15
+ export interface IAcmeDirectoryMeta {
16
+ termsOfService?: string;
17
+ website?: string;
18
+ caaIdentities?: string[];
19
+ externalAccountRequired?: boolean;
20
+ }
21
+
22
+ export interface IAcmeIdentifier {
23
+ type: 'dns';
24
+ value: string;
25
+ }
26
+
27
+ export interface IAcmeAccount {
28
+ status: string;
29
+ contact?: string[];
30
+ termsOfServiceAgreed?: boolean;
31
+ orders?: string;
32
+ }
33
+
34
+ export interface IAcmeAccountCreateRequest {
35
+ termsOfServiceAgreed: boolean;
36
+ contact?: string[];
37
+ }
38
+
39
+ export interface IAcmeOrder {
40
+ url: string;
41
+ status: string;
42
+ expires?: string;
43
+ identifiers: IAcmeIdentifier[];
44
+ authorizations: string[];
45
+ finalize: string;
46
+ certificate?: string;
47
+ }
48
+
49
+ export interface IAcmeAuthorization {
50
+ identifier: IAcmeIdentifier;
51
+ status: string;
52
+ expires?: string;
53
+ challenges: IAcmeChallenge[];
54
+ wildcard?: boolean;
55
+ }
56
+
57
+ export interface IAcmeChallenge {
58
+ type: string;
59
+ url: string;
60
+ status: string;
61
+ token: string;
62
+ validated?: string;
63
+ }
64
+
65
+ export interface IAcmeCsrOptions {
66
+ commonName: string;
67
+ altNames?: string[];
68
+ }
69
+
70
+ export interface IAcmeHttpResponse {
71
+ status: number;
72
+ headers: Record<string, string>;
73
+ data: any;
74
+ }
@@ -0,0 +1,16 @@
1
+ export { AcmeClient, type IAcmeClientOptions } from './acme.classes.client.js';
2
+ export { AcmeCrypto } from './acme.classes.crypto.js';
3
+ export { AcmeError } from './acme.classes.error.js';
4
+ export { ACME_DIRECTORY_URLS } from './acme.classes.directory.js';
5
+ export type {
6
+ IAcmeDirectory,
7
+ IAcmeDirectoryMeta,
8
+ IAcmeIdentifier,
9
+ IAcmeAccount,
10
+ IAcmeAccountCreateRequest,
11
+ IAcmeOrder,
12
+ IAcmeAuthorization,
13
+ IAcmeChallenge,
14
+ IAcmeCsrOptions,
15
+ IAcmeHttpResponse,
16
+ } from './acme.interfaces.js';
@@ -14,7 +14,6 @@ export class MongoCertManager implements ICertManager {
14
14
  */
15
15
  constructor(mongoDescriptor: plugins.smartdata.IMongoDescriptor) {
16
16
  this.db = new plugins.smartdata.SmartdataDb(mongoDescriptor);
17
- // Use a single EasyStore document to hold all certs keyed by domainName
18
17
  this.store = new plugins.smartdata.EasyStore<Record<string, any>>(
19
18
  'smartacme-certs',
20
19
  this.db,
package/ts/plugins.ts CHANGED
@@ -9,21 +9,13 @@ import * as cloudflare from '@apiclient.xyz/cloudflare';
9
9
 
10
10
  export { cloudflare };
11
11
 
12
- // @apiglobal scope
13
- import * as typedserver from '@api.global/typedserver';
14
-
15
- export { typedserver };
16
-
17
- // @pushrocks scope
12
+ // @push.rocks scope
18
13
  import * as lik from '@push.rocks/lik';
19
14
  import * as smartdata from '@push.rocks/smartdata';
20
15
  import * as smartdelay from '@push.rocks/smartdelay';
21
16
  import * as smartdnsClient from '@push.rocks/smartdns/client';
22
- import * as smartfile from '@push.rocks/smartfile';
23
17
  import * as smartlog from '@push.rocks/smartlog';
24
18
  import * as smartnetwork from '@push.rocks/smartnetwork';
25
- import * as smartpromise from '@push.rocks/smartpromise';
26
- import * as smartrequest from '@push.rocks/smartrequest';
27
19
  import * as smartunique from '@push.rocks/smartunique';
28
20
  import * as smartstring from '@push.rocks/smartstring';
29
21
  import * as smarttime from '@push.rocks/smarttime';
@@ -33,11 +25,8 @@ export {
33
25
  smartdata,
34
26
  smartdelay,
35
27
  smartdnsClient,
36
- smartfile,
37
28
  smartlog,
38
29
  smartnetwork,
39
- smartpromise,
40
- smartrequest,
41
30
  smartunique,
42
31
  smartstring,
43
32
  smarttime,
@@ -48,8 +37,8 @@ import * as tsclass from '@tsclass/tsclass';
48
37
 
49
38
  export { tsclass };
50
39
 
51
- // third party scope
52
- import * as acme from 'acme-client';
40
+ // acme protocol (custom implementation)
41
+ import * as acme from './acme/index.js';
53
42
 
54
43
  export { acme };
55
44
  // local handlers for challenge types
@@ -54,7 +54,7 @@ export class SmartAcme {
54
54
  private options: ISmartAcmeOptions;
55
55
 
56
56
  // the acme client
57
- private client: plugins.acme.Client;
57
+ private client: plugins.acme.AcmeClient;
58
58
  private smartdns = new plugins.smartdnsClient.Smartdns({});
59
59
  public logger: plugins.smartlog.Smartlog;
60
60
 
@@ -77,6 +77,9 @@ export class SmartAcme {
77
77
  private challengePriority: string[];
78
78
  // Map for coordinating concurrent certificate requests
79
79
  private interestMap: plugins.lik.InterestMap<string, SmartacmeCert>;
80
+ // bound signal handlers so they can be removed on stop()
81
+ private boundSigintHandler: (() => void) | null = null;
82
+ private boundSigtermHandler: (() => void) | null = null;
80
83
 
81
84
  constructor(optionsArg: ISmartAcmeOptions) {
82
85
  this.options = optionsArg;
@@ -114,7 +117,7 @@ export class SmartAcme {
114
117
  */
115
118
  public async start() {
116
119
  this.privateKey =
117
- this.options.accountPrivateKey || (await plugins.acme.forge.createPrivateKey()).toString();
120
+ this.options.accountPrivateKey || plugins.acme.AcmeCrypto.createRsaPrivateKey();
118
121
 
119
122
  // Initialize certificate manager
120
123
  if (!this.options.certManager) {
@@ -127,15 +130,18 @@ export class SmartAcme {
127
130
  this.certmatcher = new SmartacmeCertMatcher();
128
131
 
129
132
  // ACME Client
130
- this.client = new plugins.acme.Client({
133
+ this.client = new plugins.acme.AcmeClient({
131
134
  directoryUrl: (() => {
132
135
  if (this.options.environment === 'production') {
133
- return plugins.acme.directory.letsencrypt.production;
136
+ return plugins.acme.ACME_DIRECTORY_URLS.letsencrypt.production;
134
137
  } else {
135
- return plugins.acme.directory.letsencrypt.staging;
138
+ return plugins.acme.ACME_DIRECTORY_URLS.letsencrypt.staging;
136
139
  }
137
140
  })(),
138
- accountKey: this.privateKey,
141
+ accountKeyPem: this.privateKey,
142
+ logger: (level, message, data) => {
143
+ this.logger.log(level as any, message, data);
144
+ },
139
145
  });
140
146
 
141
147
  /* Register account */
@@ -143,20 +149,31 @@ export class SmartAcme {
143
149
  termsOfServiceAgreed: true,
144
150
  contact: [`mailto:${this.options.accountEmail}`],
145
151
  });
146
- // Setup graceful shutdown handlers
147
- process.on('SIGINT', () => this.handleSignal('SIGINT'));
148
- process.on('SIGTERM', () => this.handleSignal('SIGTERM'));
152
+ // Setup graceful shutdown handlers (store references for removal in stop())
153
+ this.boundSigintHandler = () => this.handleSignal('SIGINT');
154
+ this.boundSigtermHandler = () => this.handleSignal('SIGTERM');
155
+ process.on('SIGINT', this.boundSigintHandler);
156
+ process.on('SIGTERM', this.boundSigtermHandler);
149
157
  }
150
158
 
151
159
  /**
152
160
  * Stops the SmartAcme instance and closes certificate store connections.
153
161
  */
154
162
  public async stop() {
163
+ // Remove signal handlers so the process can exit cleanly
164
+ if (this.boundSigintHandler) {
165
+ process.removeListener('SIGINT', this.boundSigintHandler);
166
+ this.boundSigintHandler = null;
167
+ }
168
+ if (this.boundSigtermHandler) {
169
+ process.removeListener('SIGTERM', this.boundSigtermHandler);
170
+ this.boundSigtermHandler = null;
171
+ }
155
172
  if (this.certmanager && typeof (this.certmanager as any).close === 'function') {
156
173
  await (this.certmanager as any).close();
157
174
  }
158
175
  }
159
- /** Retry helper with exponential backoff */
176
+ /** Retry helper with exponential backoff and AcmeError awareness */
160
177
  private async retry<T>(operation: () => Promise<T>, operationName: string = 'operation'): Promise<T> {
161
178
  let attempt = 0;
162
179
  let delay = this.retryOptions.minTimeoutMs;
@@ -164,6 +181,19 @@ export class SmartAcme {
164
181
  try {
165
182
  return await operation();
166
183
  } catch (err) {
184
+ // Check if it's a non-retryable ACME error — throw immediately
185
+ if (err instanceof plugins.acme.AcmeError) {
186
+ if (!err.isRetryable) {
187
+ await this.logger.log('error', `Operation ${operationName} failed with non-retryable error (${err.type}, HTTP ${err.status}) at ${err.url}`, err);
188
+ throw err;
189
+ }
190
+ // For rate-limited errors, use server-specified Retry-After delay
191
+ if (err.isRateLimited && err.retryAfter > 0) {
192
+ delay = err.retryAfter * 1000;
193
+ await this.logger.log('warn', `Operation ${operationName} rate-limited, Retry-After: ${err.retryAfter}s`, err);
194
+ }
195
+ }
196
+
167
197
  attempt++;
168
198
  if (attempt > this.retryOptions.retries) {
169
199
  await this.logger.log('error', `Operation ${operationName} failed after ${attempt} attempts`, err);
@@ -347,11 +377,6 @@ export class SmartAcme {
347
377
  this.logger.log('info', 'Cooling down for 1 minute before ACME verification');
348
378
  await plugins.smartdelay.delayFor(60000);
349
379
  }
350
- // Official ACME verification (ensures challenge is publicly reachable)
351
- await this.retry(
352
- () => this.client.verifyChallenge(authz, selectedChallengeArg),
353
- `${type}.verifyChallenge`,
354
- );
355
380
  // Notify ACME server to complete the challenge
356
381
  await this.retry(
357
382
  () => this.client.completeChallenge(selectedChallengeArg),
@@ -399,7 +424,7 @@ export class SmartAcme {
399
424
  }
400
425
  }
401
426
 
402
- const [key, csr] = await plugins.acme.forge.createCsr({
427
+ const [key, csr] = await plugins.acme.AcmeCrypto.createCsr({
403
428
  commonName,
404
429
  altNames: csrDomains,
405
430
  });
@@ -1,42 +0,0 @@
1
- import * as plugins from './smartacme.plugins.js';
2
- import type { ICertManager } from './interfaces/certmanager.js';
3
- import { SmartacmeCert } from './smartacme.classes.cert.js';
4
- /**
5
- * In-memory certificate manager for mongoless mode.
6
- * Stores certificates in memory only and does not connect to MongoDB.
7
- */
8
- export declare class MemoryCertManager implements ICertManager {
9
- interestMap: plugins.lik.InterestMap<string, SmartacmeCert>;
10
- private certs;
11
- constructor();
12
- init(): Promise<void>;
13
- retrieveCertificate(domainName: string): Promise<SmartacmeCert | null>;
14
- storeCertificate(cert: SmartacmeCert): Promise<void>;
15
- deleteCertificate(domainName: string): Promise<void>;
16
- close(): Promise<void>;
17
- /**
18
- * Wipe all certificates from the in-memory store (for testing)
19
- */
20
- wipe(): Promise<void>;
21
- }
22
- /**
23
- * MongoDB-backed certificate manager using EasyStore from smartdata.
24
- */
25
- export declare class MongoCertManager implements ICertManager {
26
- interestMap: plugins.lik.InterestMap<string, SmartacmeCert>;
27
- private db;
28
- private store;
29
- /**
30
- * @param mongoDescriptor MongoDB connection settings
31
- */
32
- constructor(mongoDescriptor: plugins.smartdata.IMongoDescriptor);
33
- init(): Promise<void>;
34
- retrieveCertificate(domainName: string): Promise<SmartacmeCert | null>;
35
- storeCertificate(cert: SmartacmeCert): Promise<void>;
36
- deleteCertificate(domainName: string): Promise<void>;
37
- close(): Promise<void>;
38
- /**
39
- * Wipe all certificates from the persistent store (for integration testing)
40
- */
41
- wipe(): Promise<void>;
42
- }
@@ -1,86 +0,0 @@
1
- import * as plugins from './smartacme.plugins.js';
2
- import { SmartacmeCert } from './smartacme.classes.cert.js';
3
- /**
4
- * In-memory certificate manager for mongoless mode.
5
- * Stores certificates in memory only and does not connect to MongoDB.
6
- */
7
- export class MemoryCertManager {
8
- constructor() {
9
- this.certs = new Map();
10
- this.interestMap = new plugins.lik.InterestMap((domain) => domain);
11
- }
12
- async init() {
13
- // no-op for in-memory store
14
- }
15
- async retrieveCertificate(domainName) {
16
- return this.certs.get(domainName) ?? null;
17
- }
18
- async storeCertificate(cert) {
19
- this.certs.set(cert.domainName, cert);
20
- const interest = this.interestMap.findInterest(cert.domainName);
21
- if (interest) {
22
- interest.fullfillInterest(cert);
23
- interest.markLost();
24
- }
25
- }
26
- async deleteCertificate(domainName) {
27
- this.certs.delete(domainName);
28
- }
29
- async close() {
30
- // no-op
31
- }
32
- /**
33
- * Wipe all certificates from the in-memory store (for testing)
34
- */
35
- async wipe() {
36
- this.certs.clear();
37
- // reset interest map
38
- this.interestMap = new plugins.lik.InterestMap((domain) => domain);
39
- }
40
- }
41
- /**
42
- * MongoDB-backed certificate manager using EasyStore from smartdata.
43
- */
44
- export class MongoCertManager {
45
- /**
46
- * @param mongoDescriptor MongoDB connection settings
47
- */
48
- constructor(mongoDescriptor) {
49
- this.db = new plugins.smartdata.SmartdataDb(mongoDescriptor);
50
- // Use a single EasyStore document to hold all certs keyed by domainName
51
- this.store = new plugins.smartdata.EasyStore('smartacme-certs', this.db);
52
- this.interestMap = new plugins.lik.InterestMap((domain) => domain);
53
- }
54
- async init() {
55
- await this.db.init();
56
- }
57
- async retrieveCertificate(domainName) {
58
- const data = await this.store.readKey(domainName);
59
- return data ? new SmartacmeCert(data) : null;
60
- }
61
- async storeCertificate(cert) {
62
- // write plain object for persistence
63
- await this.store.writeKey(cert.domainName, { ...cert });
64
- const interest = this.interestMap.findInterest(cert.domainName);
65
- if (interest) {
66
- interest.fullfillInterest(cert);
67
- interest.markLost();
68
- }
69
- }
70
- async deleteCertificate(domainName) {
71
- await this.store.deleteKey(domainName);
72
- }
73
- async close() {
74
- await this.db.close();
75
- }
76
- /**
77
- * Wipe all certificates from the persistent store (for integration testing)
78
- */
79
- async wipe() {
80
- // clear all keys in the easy store
81
- await this.store.wipe();
82
- // reset interest map
83
- this.interestMap = new plugins.lik.InterestMap((domain) => domain);
84
- }
85
- }
86
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2VydG1hbmFnZXJzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2VydG1hbmFnZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sd0JBQXdCLENBQUM7QUFFbEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRTVEOzs7R0FHRztBQUNILE1BQU0sT0FBTyxpQkFBaUI7SUFJNUI7UUFGUSxVQUFLLEdBQStCLElBQUksR0FBRyxFQUFFLENBQUM7UUFHcEQsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNyRSxDQUFDO0lBRU0sS0FBSyxDQUFDLElBQUk7UUFDZiw0QkFBNEI7SUFDOUIsQ0FBQztJQUVNLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxVQUFrQjtRQUNqRCxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxJQUFJLElBQUksQ0FBQztJQUM1QyxDQUFDO0lBRU0sS0FBSyxDQUFDLGdCQUFnQixDQUFDLElBQW1CO1FBQy9DLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDdEMsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ2hFLElBQUksUUFBUSxFQUFFLENBQUM7WUFDYixRQUFRLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDaEMsUUFBUSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQ3RCLENBQUM7SUFDSCxDQUFDO0lBRU0sS0FBSyxDQUFDLGlCQUFpQixDQUFDLFVBQWtCO1FBQy9DLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxD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
@@ -1,43 +0,0 @@
1
- import * as plugins from './smartacme.plugins.js';
2
- import { SmartacmeCert } from './smartacme.classes.cert.js';
3
- import { SmartAcme } from './smartacme.classes.smartacme.js';
4
- export declare class SmartacmeCertManager {
5
- static activeDB: plugins.smartdata.SmartdataDb;
6
- private mongoDescriptor;
7
- smartdataDb: plugins.smartdata.SmartdataDb;
8
- interestMap: plugins.lik.InterestMap<string, SmartacmeCert>;
9
- constructor(smartAcmeArg: SmartAcme, optionsArg: {
10
- mongoDescriptor: plugins.smartdata.IMongoDescriptor;
11
- });
12
- init(): Promise<void>;
13
- /**
14
- * retrieves a certificate
15
- * @returns the Cert class or null
16
- * @param certDomainNameArg the domain Name to retrieve the vcertificate for
17
- */
18
- retrieveCertificate(certDomainNameArg: string): Promise<SmartacmeCert>;
19
- /**
20
- * stores the certificate
21
- * @param optionsArg
22
- */
23
- storeCertificate(optionsArg: plugins.tsclass.network.ICert): Promise<void>;
24
- deleteCertificate(certDomainNameArg: string): Promise<void>;
25
- /**
26
- * Close underlying MongoDB connection
27
- */
28
- close(): Promise<void>;
29
- }
30
- /**
31
- * In-memory certificate manager for mongoless mode.
32
- * Stores certificates in memory only and does not connect to MongoDB.
33
- */
34
- export declare class MemoryCertManager {
35
- interestMap: plugins.lik.InterestMap<string, SmartacmeCert>;
36
- private certs;
37
- constructor();
38
- init(): Promise<void>;
39
- retrieveCertificate(certDomainName: string): Promise<SmartacmeCert>;
40
- storeCertificate(optionsArg: plugins.tsclass.network.ICert): Promise<void>;
41
- deleteCertificate(certDomainName: string): Promise<void>;
42
- close(): Promise<void>;
43
- }
@@ -1,92 +0,0 @@
1
- import * as plugins from './smartacme.plugins.js';
2
- import { SmartacmeCert } from './smartacme.classes.cert.js';
3
- import { SmartAcme } from './smartacme.classes.smartacme.js';
4
- import * as interfaces from './interfaces/index.js';
5
- export class SmartacmeCertManager {
6
- constructor(smartAcmeArg, optionsArg) {
7
- this.mongoDescriptor = optionsArg.mongoDescriptor;
8
- }
9
- async init() {
10
- // Smartdata DB
11
- this.smartdataDb = new plugins.smartdata.SmartdataDb(this.mongoDescriptor);
12
- await this.smartdataDb.init();
13
- SmartacmeCertManager.activeDB = this.smartdataDb;
14
- // Pending Map
15
- this.interestMap = new plugins.lik.InterestMap((certName) => certName);
16
- }
17
- /**
18
- * retrieves a certificate
19
- * @returns the Cert class or null
20
- * @param certDomainNameArg the domain Name to retrieve the vcertificate for
21
- */
22
- async retrieveCertificate(certDomainNameArg) {
23
- const existingCertificate = await SmartacmeCert.getInstance({
24
- domainName: certDomainNameArg,
25
- });
26
- if (existingCertificate) {
27
- return existingCertificate;
28
- }
29
- else {
30
- return null;
31
- }
32
- }
33
- /**
34
- * stores the certificate
35
- * @param optionsArg
36
- */
37
- async storeCertificate(optionsArg) {
38
- const cert = new SmartacmeCert(optionsArg);
39
- await cert.save();
40
- const interest = this.interestMap.findInterest(cert.domainName);
41
- if (interest) {
42
- interest.fullfillInterest(cert);
43
- interest.markLost();
44
- }
45
- }
46
- async deleteCertificate(certDomainNameArg) {
47
- const cert = await SmartacmeCert.getInstance({
48
- domainName: certDomainNameArg,
49
- });
50
- await cert.delete();
51
- }
52
- /**
53
- * Close underlying MongoDB connection
54
- */
55
- async close() {
56
- await this.smartdataDb.close();
57
- }
58
- }
59
- /**
60
- * In-memory certificate manager for mongoless mode.
61
- * Stores certificates in memory only and does not connect to MongoDB.
62
- */
63
- export class MemoryCertManager {
64
- constructor() {
65
- this.certs = new Map();
66
- this.interestMap = new plugins.lik.InterestMap((domain) => domain);
67
- }
68
- async init() {
69
- // no-op for in-memory store
70
- return;
71
- }
72
- async retrieveCertificate(certDomainName) {
73
- return this.certs.get(certDomainName) ?? null;
74
- }
75
- async storeCertificate(optionsArg) {
76
- const cert = new SmartacmeCert(optionsArg);
77
- this.certs.set(cert.domainName, cert);
78
- const interest = this.interestMap.findInterest(cert.domainName);
79
- if (interest) {
80
- interest.fullfillInterest(cert);
81
- interest.markLost();
82
- }
83
- }
84
- async deleteCertificate(certDomainName) {
85
- this.certs.delete(certDomainName);
86
- }
87
- async close() {
88
- // no-op
89
- return;
90
- }
91
- }
92
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuY2VydG1hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGFjbWUuY2xhc3Nlcy5jZXJ0bWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHdCQUF3QixDQUFDO0FBQ2xELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFFN0QsT0FBTyxLQUFLLFVBQVUsTUFBTSx1QkFBdUIsQ0FBQztBQUVwRCxNQUFNLE9BQU8sb0JBQW9CO0lBYy9CLFlBQ0UsWUFBdUIsRUFDdkIsVUFFQztRQUVELElBQUksQ0FBQyxlQUFlLEdBQUcsVUFBVSxDQUFDLGVBQWUsQ0FBQztJQUNwRCxDQUFDO0lBRU0sS0FBSyxDQUFDLElBQUk7UUFDZixlQUFlO1FBQ2YsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLE9BQU8sQ0FBQyxTQUFTLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUMzRSxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDOUIsb0JBQW9CLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUM7UUFFakQsY0FBYztRQUNkLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLFdBQVcsQ0FBQyxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDekUsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsbUJBQW1CLENBQUMsaUJBQXlCO1FBQ3hELE1BQU0sbUJBQW1CLEdBQWtCLE1BQU0sYUFBYSxDQUFDLFdBQVcsQ0FBZ0I7WUFDeEYsVUFBVSxFQUFFLGlCQUFpQjtTQUM5QixDQUFDLENBQUM7UUFFSCxJQUFJLG1CQUFtQixF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