@push.rocks/smartproxy 18.0.2 → 18.2.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 (53) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/certificate/certificate-manager.d.ts +150 -0
  3. package/dist_ts/certificate/certificate-manager.js +505 -0
  4. package/dist_ts/certificate/events/simplified-events.d.ts +56 -0
  5. package/dist_ts/certificate/events/simplified-events.js +13 -0
  6. package/dist_ts/certificate/models/certificate-errors.d.ts +69 -0
  7. package/dist_ts/certificate/models/certificate-errors.js +141 -0
  8. package/dist_ts/certificate/models/certificate-strategy.d.ts +60 -0
  9. package/dist_ts/certificate/models/certificate-strategy.js +73 -0
  10. package/dist_ts/certificate/simplified-certificate-manager.d.ts +150 -0
  11. package/dist_ts/certificate/simplified-certificate-manager.js +501 -0
  12. package/dist_ts/http/index.d.ts +1 -9
  13. package/dist_ts/http/index.js +5 -11
  14. package/dist_ts/plugins.d.ts +3 -1
  15. package/dist_ts/plugins.js +4 -2
  16. package/dist_ts/proxies/network-proxy/network-proxy.js +3 -1
  17. package/dist_ts/proxies/network-proxy/simplified-certificate-bridge.d.ts +48 -0
  18. package/dist_ts/proxies/network-proxy/simplified-certificate-bridge.js +76 -0
  19. package/dist_ts/proxies/network-proxy/websocket-handler.js +41 -4
  20. package/dist_ts/proxies/smart-proxy/cert-store.d.ts +10 -0
  21. package/dist_ts/proxies/smart-proxy/cert-store.js +70 -0
  22. package/dist_ts/proxies/smart-proxy/certificate-manager.d.ts +116 -0
  23. package/dist_ts/proxies/smart-proxy/certificate-manager.js +401 -0
  24. package/dist_ts/proxies/smart-proxy/legacy-smart-proxy.d.ts +168 -0
  25. package/dist_ts/proxies/smart-proxy/legacy-smart-proxy.js +642 -0
  26. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +26 -0
  27. package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
  28. package/dist_ts/proxies/smart-proxy/models/simplified-smartproxy-config.d.ts +65 -0
  29. package/dist_ts/proxies/smart-proxy/models/simplified-smartproxy-config.js +31 -0
  30. package/dist_ts/proxies/smart-proxy/models/smartproxy-options.d.ts +102 -0
  31. package/dist_ts/proxies/smart-proxy/models/smartproxy-options.js +73 -0
  32. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.d.ts +10 -44
  33. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.js +66 -202
  34. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +4 -0
  35. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +62 -2
  36. package/dist_ts/proxies/smart-proxy/simplified-smart-proxy.d.ts +41 -0
  37. package/dist_ts/proxies/smart-proxy/simplified-smart-proxy.js +132 -0
  38. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +18 -13
  39. package/dist_ts/proxies/smart-proxy/smart-proxy.js +79 -196
  40. package/package.json +7 -5
  41. package/readme.md +224 -10
  42. package/readme.plan.md +1405 -617
  43. package/ts/00_commitinfo_data.ts +1 -1
  44. package/ts/http/index.ts +5 -12
  45. package/ts/plugins.ts +4 -1
  46. package/ts/proxies/network-proxy/network-proxy.ts +3 -0
  47. package/ts/proxies/network-proxy/websocket-handler.ts +38 -3
  48. package/ts/proxies/smart-proxy/cert-store.ts +86 -0
  49. package/ts/proxies/smart-proxy/certificate-manager.ts +506 -0
  50. package/ts/proxies/smart-proxy/models/route-types.ts +33 -3
  51. package/ts/proxies/smart-proxy/network-proxy-bridge.ts +86 -239
  52. package/ts/proxies/smart-proxy/route-connection-handler.ts +74 -1
  53. package/ts/proxies/smart-proxy/smart-proxy.ts +105 -222
@@ -0,0 +1,401 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { NetworkProxy } from '../network-proxy/index.js';
3
+ import { CertStore } from './cert-store.js';
4
+ export class SmartCertManager {
5
+ constructor(routes, certDir = './certs', acmeOptions) {
6
+ this.routes = routes;
7
+ this.certDir = certDir;
8
+ this.acmeOptions = acmeOptions;
9
+ this.smartAcme = null;
10
+ this.networkProxy = null;
11
+ this.renewalTimer = null;
12
+ this.pendingChallenges = new Map();
13
+ this.challengeRoute = null;
14
+ // Track certificate status by route name
15
+ this.certStatus = new Map();
16
+ this.certStore = new CertStore(certDir);
17
+ }
18
+ setNetworkProxy(networkProxy) {
19
+ this.networkProxy = networkProxy;
20
+ }
21
+ /**
22
+ * Set callback for updating routes (used for challenge routes)
23
+ */
24
+ setUpdateRoutesCallback(callback) {
25
+ this.updateRoutesCallback = callback;
26
+ }
27
+ /**
28
+ * Initialize certificate manager and provision certificates for all routes
29
+ */
30
+ async initialize() {
31
+ // Create certificate directory if it doesn't exist
32
+ await this.certStore.initialize();
33
+ // Initialize SmartAcme if we have any ACME routes
34
+ const hasAcmeRoutes = this.routes.some(r => r.action.tls?.certificate === 'auto');
35
+ if (hasAcmeRoutes && this.acmeOptions?.email) {
36
+ // Create HTTP-01 challenge handler
37
+ const http01Handler = new plugins.smartacme.handlers.Http01MemoryHandler();
38
+ // Set up challenge handler integration with our routing
39
+ this.setupChallengeHandler(http01Handler);
40
+ // Create SmartAcme instance with built-in MemoryCertManager and HTTP-01 handler
41
+ this.smartAcme = new plugins.smartacme.SmartAcme({
42
+ accountEmail: this.acmeOptions.email,
43
+ environment: this.acmeOptions.useProduction ? 'production' : 'integration',
44
+ certManager: new plugins.smartacme.certmanagers.MemoryCertManager(),
45
+ challengeHandlers: [http01Handler]
46
+ });
47
+ await this.smartAcme.start();
48
+ }
49
+ // Provision certificates for all routes
50
+ await this.provisionAllCertificates();
51
+ // Start renewal timer
52
+ this.startRenewalTimer();
53
+ }
54
+ /**
55
+ * Provision certificates for all routes that need them
56
+ */
57
+ async provisionAllCertificates() {
58
+ const certRoutes = this.routes.filter(r => r.action.tls?.mode === 'terminate' ||
59
+ r.action.tls?.mode === 'terminate-and-reencrypt');
60
+ for (const route of certRoutes) {
61
+ try {
62
+ await this.provisionCertificate(route);
63
+ }
64
+ catch (error) {
65
+ console.error(`Failed to provision certificate for route ${route.name}: ${error}`);
66
+ }
67
+ }
68
+ }
69
+ /**
70
+ * Provision certificate for a single route
71
+ */
72
+ async provisionCertificate(route) {
73
+ const tls = route.action.tls;
74
+ if (!tls || (tls.mode !== 'terminate' && tls.mode !== 'terminate-and-reencrypt')) {
75
+ return;
76
+ }
77
+ const domains = this.extractDomainsFromRoute(route);
78
+ if (domains.length === 0) {
79
+ console.warn(`Route ${route.name} has TLS termination but no domains`);
80
+ return;
81
+ }
82
+ const primaryDomain = domains[0];
83
+ if (tls.certificate === 'auto') {
84
+ // ACME certificate
85
+ await this.provisionAcmeCertificate(route, domains);
86
+ }
87
+ else if (typeof tls.certificate === 'object') {
88
+ // Static certificate
89
+ await this.provisionStaticCertificate(route, primaryDomain, tls.certificate);
90
+ }
91
+ }
92
+ /**
93
+ * Provision ACME certificate
94
+ */
95
+ async provisionAcmeCertificate(route, domains) {
96
+ if (!this.smartAcme) {
97
+ throw new Error('SmartAcme not initialized');
98
+ }
99
+ const primaryDomain = domains[0];
100
+ const routeName = route.name || primaryDomain;
101
+ // Check if we already have a valid certificate
102
+ const existingCert = await this.certStore.getCertificate(routeName);
103
+ if (existingCert && this.isCertificateValid(existingCert)) {
104
+ console.log(`Using existing valid certificate for ${primaryDomain}`);
105
+ await this.applyCertificate(primaryDomain, existingCert);
106
+ this.updateCertStatus(routeName, 'valid', 'acme', existingCert);
107
+ return;
108
+ }
109
+ console.log(`Requesting ACME certificate for ${domains.join(', ')}`);
110
+ this.updateCertStatus(routeName, 'pending', 'acme');
111
+ try {
112
+ // Add challenge route before requesting certificate
113
+ await this.addChallengeRoute();
114
+ try {
115
+ // Use smartacme to get certificate
116
+ const cert = await this.smartAcme.getCertificateForDomain(primaryDomain);
117
+ // SmartAcme's Cert object has these properties:
118
+ // - publicKey: The certificate PEM string
119
+ // - privateKey: The private key PEM string
120
+ // - csr: Certificate signing request
121
+ // - validUntil: Timestamp in milliseconds
122
+ // - domainName: The domain name
123
+ const certData = {
124
+ cert: cert.publicKey,
125
+ key: cert.privateKey,
126
+ ca: cert.publicKey, // Use same as cert for now
127
+ expiryDate: new Date(cert.validUntil),
128
+ issueDate: new Date(cert.created)
129
+ };
130
+ await this.certStore.saveCertificate(routeName, certData);
131
+ await this.applyCertificate(primaryDomain, certData);
132
+ this.updateCertStatus(routeName, 'valid', 'acme', certData);
133
+ console.log(`Successfully provisioned ACME certificate for ${primaryDomain}`);
134
+ }
135
+ catch (error) {
136
+ console.error(`Failed to provision ACME certificate for ${primaryDomain}: ${error}`);
137
+ this.updateCertStatus(routeName, 'error', 'acme', undefined, error.message);
138
+ throw error;
139
+ }
140
+ finally {
141
+ // Always remove challenge route after provisioning
142
+ await this.removeChallengeRoute();
143
+ }
144
+ }
145
+ catch (error) {
146
+ // Handle outer try-catch from adding challenge route
147
+ console.error(`Failed to setup ACME challenge for ${primaryDomain}: ${error}`);
148
+ this.updateCertStatus(routeName, 'error', 'acme', undefined, error.message);
149
+ throw error;
150
+ }
151
+ }
152
+ /**
153
+ * Provision static certificate
154
+ */
155
+ async provisionStaticCertificate(route, domain, certConfig) {
156
+ const routeName = route.name || domain;
157
+ try {
158
+ let key = certConfig.key;
159
+ let cert = certConfig.cert;
160
+ // Load from files if paths are provided
161
+ if (certConfig.keyFile) {
162
+ const keyFile = await plugins.smartfile.SmartFile.fromFilePath(certConfig.keyFile);
163
+ key = keyFile.contents.toString();
164
+ }
165
+ if (certConfig.certFile) {
166
+ const certFile = await plugins.smartfile.SmartFile.fromFilePath(certConfig.certFile);
167
+ cert = certFile.contents.toString();
168
+ }
169
+ // Parse certificate to get dates
170
+ // Parse certificate to get dates - for now just use defaults
171
+ // TODO: Implement actual certificate parsing if needed
172
+ const certInfo = { validTo: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), validFrom: new Date() };
173
+ const certData = {
174
+ cert,
175
+ key,
176
+ expiryDate: certInfo.validTo,
177
+ issueDate: certInfo.validFrom
178
+ };
179
+ // Save to store for consistency
180
+ await this.certStore.saveCertificate(routeName, certData);
181
+ await this.applyCertificate(domain, certData);
182
+ this.updateCertStatus(routeName, 'valid', 'static', certData);
183
+ console.log(`Successfully loaded static certificate for ${domain}`);
184
+ }
185
+ catch (error) {
186
+ console.error(`Failed to provision static certificate for ${domain}: ${error}`);
187
+ this.updateCertStatus(routeName, 'error', 'static', undefined, error.message);
188
+ throw error;
189
+ }
190
+ }
191
+ /**
192
+ * Apply certificate to NetworkProxy
193
+ */
194
+ async applyCertificate(domain, certData) {
195
+ if (!this.networkProxy) {
196
+ console.warn('NetworkProxy not set, cannot apply certificate');
197
+ return;
198
+ }
199
+ // Apply certificate to NetworkProxy
200
+ this.networkProxy.updateCertificate(domain, certData.cert, certData.key);
201
+ // Also apply for wildcard if it's a subdomain
202
+ if (domain.includes('.') && !domain.startsWith('*.')) {
203
+ const parts = domain.split('.');
204
+ if (parts.length >= 2) {
205
+ const wildcardDomain = `*.${parts.slice(-2).join('.')}`;
206
+ this.networkProxy.updateCertificate(wildcardDomain, certData.cert, certData.key);
207
+ }
208
+ }
209
+ }
210
+ /**
211
+ * Extract domains from route configuration
212
+ */
213
+ extractDomainsFromRoute(route) {
214
+ if (!route.match.domains) {
215
+ return [];
216
+ }
217
+ const domains = Array.isArray(route.match.domains)
218
+ ? route.match.domains
219
+ : [route.match.domains];
220
+ // Filter out wildcards and patterns
221
+ return domains.filter(d => !d.includes('*') &&
222
+ !d.includes('{') &&
223
+ d.includes('.'));
224
+ }
225
+ /**
226
+ * Check if certificate is valid
227
+ */
228
+ isCertificateValid(cert) {
229
+ const now = new Date();
230
+ const expiryThreshold = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000); // 30 days
231
+ return cert.expiryDate > expiryThreshold;
232
+ }
233
+ /**
234
+ * Add challenge route to SmartProxy
235
+ */
236
+ async addChallengeRoute() {
237
+ if (!this.updateRoutesCallback) {
238
+ throw new Error('No route update callback set');
239
+ }
240
+ if (!this.challengeRoute) {
241
+ throw new Error('Challenge route not initialized');
242
+ }
243
+ const challengeRoute = this.challengeRoute;
244
+ const updatedRoutes = [...this.routes, challengeRoute];
245
+ await this.updateRoutesCallback(updatedRoutes);
246
+ }
247
+ /**
248
+ * Remove challenge route from SmartProxy
249
+ */
250
+ async removeChallengeRoute() {
251
+ if (!this.updateRoutesCallback) {
252
+ return;
253
+ }
254
+ const filteredRoutes = this.routes.filter(r => r.name !== 'acme-challenge');
255
+ await this.updateRoutesCallback(filteredRoutes);
256
+ }
257
+ /**
258
+ * Start renewal timer
259
+ */
260
+ startRenewalTimer() {
261
+ // Check for renewals every 12 hours
262
+ this.renewalTimer = setInterval(() => {
263
+ this.checkAndRenewCertificates();
264
+ }, 12 * 60 * 60 * 1000);
265
+ // Also do an immediate check
266
+ this.checkAndRenewCertificates();
267
+ }
268
+ /**
269
+ * Check and renew certificates that are expiring
270
+ */
271
+ async checkAndRenewCertificates() {
272
+ for (const route of this.routes) {
273
+ if (route.action.tls?.certificate === 'auto') {
274
+ const routeName = route.name || this.extractDomainsFromRoute(route)[0];
275
+ const cert = await this.certStore.getCertificate(routeName);
276
+ if (cert && !this.isCertificateValid(cert)) {
277
+ console.log(`Certificate for ${routeName} needs renewal`);
278
+ try {
279
+ await this.provisionCertificate(route);
280
+ }
281
+ catch (error) {
282
+ console.error(`Failed to renew certificate for ${routeName}: ${error}`);
283
+ }
284
+ }
285
+ }
286
+ }
287
+ }
288
+ /**
289
+ * Update certificate status
290
+ */
291
+ updateCertStatus(routeName, status, source, certData, error) {
292
+ this.certStatus.set(routeName, {
293
+ domain: routeName,
294
+ status,
295
+ source,
296
+ expiryDate: certData?.expiryDate,
297
+ issueDate: certData?.issueDate,
298
+ error
299
+ });
300
+ }
301
+ /**
302
+ * Get certificate status for a route
303
+ */
304
+ getCertificateStatus(routeName) {
305
+ return this.certStatus.get(routeName);
306
+ }
307
+ /**
308
+ * Force renewal of a certificate
309
+ */
310
+ async renewCertificate(routeName) {
311
+ const route = this.routes.find(r => r.name === routeName);
312
+ if (!route) {
313
+ throw new Error(`Route ${routeName} not found`);
314
+ }
315
+ // Remove existing certificate to force renewal
316
+ await this.certStore.deleteCertificate(routeName);
317
+ await this.provisionCertificate(route);
318
+ }
319
+ /**
320
+ * Setup challenge handler integration with SmartProxy routing
321
+ */
322
+ setupChallengeHandler(http01Handler) {
323
+ // Create a challenge route that delegates to SmartAcme's HTTP-01 handler
324
+ const challengeRoute = {
325
+ name: 'acme-challenge',
326
+ priority: 1000, // High priority
327
+ match: {
328
+ ports: 80,
329
+ path: '/.well-known/acme-challenge/*'
330
+ },
331
+ action: {
332
+ type: 'static',
333
+ handler: async (context) => {
334
+ // Extract the token from the path
335
+ const token = context.path?.split('/').pop();
336
+ if (!token) {
337
+ return { status: 404, body: 'Not found' };
338
+ }
339
+ // Create mock request/response objects for SmartAcme
340
+ const mockReq = {
341
+ url: context.path,
342
+ method: 'GET',
343
+ headers: context.headers || {}
344
+ };
345
+ let responseData = null;
346
+ const mockRes = {
347
+ statusCode: 200,
348
+ setHeader: (name, value) => { },
349
+ end: (data) => {
350
+ responseData = data;
351
+ }
352
+ };
353
+ // Use SmartAcme's handler
354
+ const handled = await new Promise((resolve) => {
355
+ http01Handler.handleRequest(mockReq, mockRes, () => {
356
+ resolve(false);
357
+ });
358
+ // Give it a moment to process
359
+ setTimeout(() => resolve(true), 100);
360
+ });
361
+ if (handled && responseData) {
362
+ return {
363
+ status: mockRes.statusCode,
364
+ headers: { 'Content-Type': 'text/plain' },
365
+ body: responseData
366
+ };
367
+ }
368
+ else {
369
+ return { status: 404, body: 'Not found' };
370
+ }
371
+ }
372
+ }
373
+ };
374
+ // Store the challenge route to add it when needed
375
+ this.challengeRoute = challengeRoute;
376
+ }
377
+ /**
378
+ * Stop certificate manager
379
+ */
380
+ async stop() {
381
+ if (this.renewalTimer) {
382
+ clearInterval(this.renewalTimer);
383
+ this.renewalTimer = null;
384
+ }
385
+ if (this.smartAcme) {
386
+ await this.smartAcme.stop();
387
+ }
388
+ // Remove any active challenge routes
389
+ if (this.pendingChallenges.size > 0) {
390
+ this.pendingChallenges.clear();
391
+ await this.removeChallengeRoute();
392
+ }
393
+ }
394
+ /**
395
+ * Get ACME options (for recreating after route updates)
396
+ */
397
+ getAcmeOptions() {
398
+ return this.acmeOptions;
399
+ }
400
+ }
401
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2VydGlmaWNhdGUtbWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvY2VydGlmaWNhdGUtbWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUV6RCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFtQjVDLE1BQU0sT0FBTyxnQkFBZ0I7SUFjM0IsWUFDVSxNQUFzQixFQUN0QixVQUFrQixTQUFTLEVBQzNCLFdBSVA7UUFOTyxXQUFNLEdBQU4sTUFBTSxDQUFnQjtRQUN0QixZQUFPLEdBQVAsT0FBTyxDQUFvQjtRQUMzQixnQkFBVyxHQUFYLFdBQVcsQ0FJbEI7UUFuQkssY0FBUyxHQUF1QyxJQUFJLENBQUM7UUFDckQsaUJBQVksR0FBd0IsSUFBSSxDQUFDO1FBQ3pDLGlCQUFZLEdBQTBCLElBQUksQ0FBQztRQUMzQyxzQkFBaUIsR0FBd0IsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUNuRCxtQkFBYyxHQUF3QixJQUFJLENBQUM7UUFFbkQseUNBQXlDO1FBQ2pDLGVBQVUsR0FBNkIsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQWN2RCxJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzFDLENBQUM7SUFFTSxlQUFlLENBQUMsWUFBMEI7UUFDL0MsSUFBSSxDQUFDLFlBQVksR0FBRyxZQUFZLENBQUM7SUFDbkMsQ0FBQztJQUVEOztPQUVHO0lBQ0ksdUJBQXVCLENBQUMsUUFBbUQ7UUFDaEYsSUFBSSxDQUFDLG9CQUFvQixHQUFHLFFBQVEsQ0FBQztJQUN2QyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsVUFBVTtRQUNyQixtREFBbUQ7UUFDbkQsTUFBTSxJ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
@@ -0,0 +1,168 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { RouteManager } from './route-manager.js';
3
+ import type { ISmartProxyOptions } from './models/interfaces.js';
4
+ import type { IRouteConfig } from './models/route-types.js';
5
+ /**
6
+ * SmartProxy - Pure route-based API
7
+ *
8
+ * SmartProxy is a unified proxy system that works with routes to define connection handling behavior.
9
+ * Each route contains matching criteria (ports, domains, etc.) and an action to take (forward, redirect, block).
10
+ *
11
+ * Configuration is provided through a set of routes, with each route defining:
12
+ * - What to match (ports, domains, paths, client IPs)
13
+ * - What to do with matching traffic (forward, redirect, block)
14
+ * - How to handle TLS (passthrough, terminate, terminate-and-reencrypt)
15
+ * - Security settings (IP restrictions, connection limits)
16
+ * - Advanced options (timeout, headers, etc.)
17
+ */
18
+ export declare class SmartProxy extends plugins.EventEmitter {
19
+ private portManager;
20
+ private connectionLogger;
21
+ private isShuttingDown;
22
+ private connectionManager;
23
+ private securityManager;
24
+ private tlsManager;
25
+ private networkProxyBridge;
26
+ private timeoutManager;
27
+ routeManager: RouteManager;
28
+ private routeConnectionHandler;
29
+ private nftablesManager;
30
+ private port80Handler;
31
+ private certProvisioner?;
32
+ /**
33
+ * Constructor for SmartProxy
34
+ *
35
+ * @param settingsArg Configuration options containing routes and other settings
36
+ * Routes define how traffic is matched and handled, with each route having:
37
+ * - match: criteria for matching traffic (ports, domains, paths, IPs)
38
+ * - action: what to do with matched traffic (forward, redirect, block)
39
+ *
40
+ * Example:
41
+ * ```ts
42
+ * const proxy = new SmartProxy({
43
+ * routes: [
44
+ * {
45
+ * match: {
46
+ * ports: 443,
47
+ * domains: ['example.com', '*.example.com']
48
+ * },
49
+ * action: {
50
+ * type: 'forward',
51
+ * target: { host: '10.0.0.1', port: 8443 },
52
+ * tls: { mode: 'passthrough' }
53
+ * }
54
+ * }
55
+ * ],
56
+ * defaults: {
57
+ * target: { host: 'localhost', port: 8080 },
58
+ * security: { ipAllowList: ['*'] }
59
+ * }
60
+ * });
61
+ * ```
62
+ */
63
+ constructor(settingsArg: ISmartProxyOptions);
64
+ /**
65
+ * The settings for the SmartProxy
66
+ */
67
+ settings: ISmartProxyOptions;
68
+ /**
69
+ * Initialize the Port80Handler for ACME certificate management
70
+ */
71
+ private initializePort80Handler;
72
+ /**
73
+ * Start the proxy server with support for both configuration types
74
+ */
75
+ start(): Promise<void>;
76
+ /**
77
+ * Extract domain configurations from routes for certificate provisioning
78
+ *
79
+ * Note: This method has been removed as we now work directly with routes
80
+ */
81
+ /**
82
+ * Stop the proxy server
83
+ */
84
+ stop(): Promise<void>;
85
+ /**
86
+ * Updates the domain configurations for the proxy
87
+ *
88
+ * Note: This legacy method has been removed. Use updateRoutes instead.
89
+ */
90
+ updateDomainConfigs(): Promise<void>;
91
+ /**
92
+ * Update routes with new configuration
93
+ *
94
+ * This method replaces the current route configuration with the provided routes.
95
+ * It also provisions certificates for routes that require TLS termination and have
96
+ * `certificate: 'auto'` set in their TLS configuration.
97
+ *
98
+ * @param newRoutes Array of route configurations to use
99
+ *
100
+ * Example:
101
+ * ```ts
102
+ * proxy.updateRoutes([
103
+ * {
104
+ * match: { ports: 443, domains: 'secure.example.com' },
105
+ * action: {
106
+ * type: 'forward',
107
+ * target: { host: '10.0.0.1', port: 8443 },
108
+ * tls: { mode: 'terminate', certificate: 'auto' }
109
+ * }
110
+ * }
111
+ * ]);
112
+ * ```
113
+ */
114
+ updateRoutes(newRoutes: IRouteConfig[]): Promise<void>;
115
+ /**
116
+ * Request a certificate for a specific domain
117
+ *
118
+ * @param domain The domain to request a certificate for
119
+ * @param routeName Optional route name to associate with the certificate
120
+ */
121
+ requestCertificate(domain: string, routeName?: string): Promise<boolean>;
122
+ /**
123
+ * Validates if a domain name is valid for certificate issuance
124
+ */
125
+ private isValidDomain;
126
+ /**
127
+ * Add a new listening port without changing the route configuration
128
+ *
129
+ * This allows you to add a port listener without updating routes.
130
+ * Useful for preparing to listen on a port before adding routes for it.
131
+ *
132
+ * @param port The port to start listening on
133
+ * @returns Promise that resolves when the port is listening
134
+ */
135
+ addListeningPort(port: number): Promise<void>;
136
+ /**
137
+ * Stop listening on a specific port without changing the route configuration
138
+ *
139
+ * This allows you to stop a port listener without updating routes.
140
+ * Useful for temporary maintenance or port changes.
141
+ *
142
+ * @param port The port to stop listening on
143
+ * @returns Promise that resolves when the port is closed
144
+ */
145
+ removeListeningPort(port: number): Promise<void>;
146
+ /**
147
+ * Get a list of all ports currently being listened on
148
+ *
149
+ * @returns Array of port numbers
150
+ */
151
+ getListeningPorts(): number[];
152
+ /**
153
+ * Get statistics about current connections
154
+ */
155
+ getStatistics(): any;
156
+ /**
157
+ * Get a list of eligible domains for ACME certificates
158
+ */
159
+ getEligibleDomainsForCertificates(): string[];
160
+ /**
161
+ * Get NFTables status
162
+ */
163
+ getNfTablesStatus(): Promise<Record<string, any>>;
164
+ /**
165
+ * Get status of certificates managed by Port80Handler
166
+ */
167
+ getCertificateStatus(): any;
168
+ }