@push.rocks/smartproxy 4.2.6 → 5.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.
@@ -118,6 +118,7 @@ export interface ICertificateExpiring {
118
118
 
119
119
  /**
120
120
  * Port80Handler with ACME certificate management and request forwarding capabilities
121
+ * Now with glob pattern support for domain matching
121
122
  */
122
123
  export class Port80Handler extends plugins.EventEmitter {
123
124
  private domainCertificates: Map<string, IDomainCertificate>;
@@ -180,6 +181,12 @@ export class Port80Handler extends plugins.EventEmitter {
180
181
 
181
182
  // Start certificate process for domains with acmeMaintenance enabled
182
183
  for (const [domain, domainInfo] of this.domainCertificates.entries()) {
184
+ // Skip glob patterns for certificate issuance
185
+ if (this.isGlobPattern(domain)) {
186
+ console.log(`Skipping initial certificate for glob pattern: ${domain}`);
187
+ continue;
188
+ }
189
+
183
190
  if (domainInfo.options.acmeMaintenance && !domainInfo.certObtained && !domainInfo.obtainingInProgress) {
184
191
  this.obtainCertificate(domain).catch(err => {
185
192
  console.error(`Error obtaining initial certificate for ${domain}:`, err);
@@ -252,8 +259,8 @@ export class Port80Handler extends plugins.EventEmitter {
252
259
  hasAcmeForward: !!options.acmeForward
253
260
  });
254
261
 
255
- // If acmeMaintenance is enabled, start certificate process immediately
256
- if (options.acmeMaintenance && this.server) {
262
+ // If acmeMaintenance is enabled and not a glob pattern, start certificate process immediately
263
+ if (options.acmeMaintenance && this.server && !this.isGlobPattern(domainName)) {
257
264
  this.obtainCertificate(domainName).catch(err => {
258
265
  console.error(`Error obtaining initial certificate for ${domainName}:`, err);
259
266
  });
@@ -288,6 +295,11 @@ export class Port80Handler extends plugins.EventEmitter {
288
295
  throw new Port80HandlerError('Domain, certificate and privateKey are required');
289
296
  }
290
297
 
298
+ // Don't allow setting certificates for glob patterns
299
+ if (this.isGlobPattern(domain)) {
300
+ throw new Port80HandlerError('Cannot set certificate for glob pattern domains');
301
+ }
302
+
291
303
  let domainInfo = this.domainCertificates.get(domain);
292
304
 
293
305
  if (!domainInfo) {
@@ -334,6 +346,11 @@ export class Port80Handler extends plugins.EventEmitter {
334
346
  * @param domain The domain to get the certificate for
335
347
  */
336
348
  public getCertificate(domain: string): ICertificateData | null {
349
+ // Can't get certificates for glob patterns
350
+ if (this.isGlobPattern(domain)) {
351
+ return null;
352
+ }
353
+
337
354
  const domainInfo = this.domainCertificates.get(domain);
338
355
 
339
356
  if (!domainInfo || !domainInfo.certObtained || !domainInfo.certificate || !domainInfo.privateKey) {
@@ -348,6 +365,65 @@ export class Port80Handler extends plugins.EventEmitter {
348
365
  };
349
366
  }
350
367
 
368
+ /**
369
+ * Check if a domain is a glob pattern
370
+ * @param domain Domain to check
371
+ * @returns True if the domain is a glob pattern
372
+ */
373
+ private isGlobPattern(domain: string): boolean {
374
+ return domain.includes('*');
375
+ }
376
+
377
+ /**
378
+ * Get domain info for a specific domain, using glob pattern matching if needed
379
+ * @param requestDomain The actual domain from the request
380
+ * @returns The domain info or null if not found
381
+ */
382
+ private getDomainInfoForRequest(requestDomain: string): { domainInfo: IDomainCertificate, pattern: string } | null {
383
+ // Try direct match first
384
+ if (this.domainCertificates.has(requestDomain)) {
385
+ return {
386
+ domainInfo: this.domainCertificates.get(requestDomain)!,
387
+ pattern: requestDomain
388
+ };
389
+ }
390
+
391
+ // Then try glob patterns
392
+ for (const [pattern, domainInfo] of this.domainCertificates.entries()) {
393
+ if (this.isGlobPattern(pattern) && this.domainMatchesPattern(requestDomain, pattern)) {
394
+ return { domainInfo, pattern };
395
+ }
396
+ }
397
+
398
+ return null;
399
+ }
400
+
401
+ /**
402
+ * Check if a domain matches a glob pattern
403
+ * @param domain The domain to check
404
+ * @param pattern The pattern to match against
405
+ * @returns True if the domain matches the pattern
406
+ */
407
+ private domainMatchesPattern(domain: string, pattern: string): boolean {
408
+ // Handle different glob pattern styles
409
+ if (pattern.startsWith('*.')) {
410
+ // *.example.com matches any subdomain
411
+ const suffix = pattern.substring(2);
412
+ return domain.endsWith(suffix) && domain.includes('.') && domain !== suffix;
413
+ } else if (pattern.endsWith('.*')) {
414
+ // example.* matches any TLD
415
+ const prefix = pattern.substring(0, pattern.length - 2);
416
+ const domainParts = domain.split('.');
417
+ return domain.startsWith(prefix + '.') && domainParts.length >= 2;
418
+ } else if (pattern === '*') {
419
+ // Wildcard matches everything
420
+ return true;
421
+ } else {
422
+ // Exact match (shouldn't reach here as we check exact matches first)
423
+ return domain === pattern;
424
+ }
425
+ }
426
+
351
427
  /**
352
428
  * Lazy initialization of the ACME client
353
429
  * @returns An ACME client instance
@@ -397,14 +473,16 @@ export class Port80Handler extends plugins.EventEmitter {
397
473
  // Extract domain (ignoring any port in the Host header)
398
474
  const domain = hostHeader.split(':')[0];
399
475
 
400
- // Check if domain is configured
401
- if (!this.domainCertificates.has(domain)) {
476
+ // Get domain config, using glob pattern matching if needed
477
+ const domainMatch = this.getDomainInfoForRequest(domain);
478
+
479
+ if (!domainMatch) {
402
480
  res.statusCode = 404;
403
481
  res.end('Domain not configured');
404
482
  return;
405
483
  }
406
484
 
407
- const domainInfo = this.domainCertificates.get(domain)!;
485
+ const { domainInfo, pattern } = domainMatch;
408
486
  const options = domainInfo.options;
409
487
 
410
488
  // If the request is for an ACME HTTP-01 challenge, handle it
@@ -415,8 +493,11 @@ export class Port80Handler extends plugins.EventEmitter {
415
493
  return;
416
494
  }
417
495
 
418
- this.handleAcmeChallenge(req, res, domain);
419
- return;
496
+ // Only handle ACME challenges for non-glob patterns
497
+ if (!this.isGlobPattern(pattern)) {
498
+ this.handleAcmeChallenge(req, res, domain);
499
+ return;
500
+ }
420
501
  }
421
502
 
422
503
  // Check if we should forward non-ACME requests
@@ -426,7 +507,8 @@ export class Port80Handler extends plugins.EventEmitter {
426
507
  }
427
508
 
428
509
  // If certificate exists and sslRedirect is enabled, redirect to HTTPS
429
- if (domainInfo.certObtained && options.sslRedirect) {
510
+ // (Skip for glob patterns as they won't have certificates)
511
+ if (!this.isGlobPattern(pattern) && domainInfo.certObtained && options.sslRedirect) {
430
512
  const httpsPort = this.options.httpsRedirectPort;
431
513
  const portSuffix = httpsPort === 443 ? '' : `:${httpsPort}`;
432
514
  const redirectUrl = `https://${domain}${portSuffix}${req.url || '/'}`;
@@ -438,7 +520,8 @@ export class Port80Handler extends plugins.EventEmitter {
438
520
  }
439
521
 
440
522
  // Handle case where certificate maintenance is enabled but not yet obtained
441
- if (options.acmeMaintenance && !domainInfo.certObtained) {
523
+ // (Skip for glob patterns as they can't have certificates)
524
+ if (!this.isGlobPattern(pattern) && options.acmeMaintenance && !domainInfo.certObtained) {
442
525
  // Trigger certificate issuance if not already running
443
526
  if (!domainInfo.obtainingInProgress) {
444
527
  this.obtainCertificate(domain).catch(err => {
@@ -559,6 +642,11 @@ export class Port80Handler extends plugins.EventEmitter {
559
642
  * @param isRenewal Whether this is a renewal attempt
560
643
  */
561
644
  private async obtainCertificate(domain: string, isRenewal: boolean = false): Promise<void> {
645
+ // Don't allow certificate issuance for glob patterns
646
+ if (this.isGlobPattern(domain)) {
647
+ throw new CertificateError('Cannot obtain certificates for glob pattern domains', domain, isRenewal);
648
+ }
649
+
562
650
  // Get the domain info
563
651
  const domainInfo = this.domainCertificates.get(domain);
564
652
  if (!domainInfo) {
@@ -752,6 +840,11 @@ export class Port80Handler extends plugins.EventEmitter {
752
840
  const renewThresholdMs = this.options.renewThresholdDays * 24 * 60 * 60 * 1000;
753
841
 
754
842
  for (const [domain, domainInfo] of this.domainCertificates.entries()) {
843
+ // Skip glob patterns
844
+ if (this.isGlobPattern(domain)) {
845
+ continue;
846
+ }
847
+
755
848
  // Skip domains with acmeMaintenance disabled
756
849
  if (!domainInfo.options.acmeMaintenance) {
757
850
  continue;
package/ts/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from './classes.iptablesproxy.js';
1
+ export * from './classes.nftablesproxy.js';
2
2
  export * from './classes.networkproxy.js';
3
3
  export * from './classes.port80handler.js';
4
4
  export * from './classes.sslredirect.js';