@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
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartproxy',
6
- version: '18.0.2',
6
+ version: '18.2.0',
7
7
  description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
8
8
  }
package/ts/http/index.ts CHANGED
@@ -5,19 +5,12 @@
5
5
  // Export types and models
6
6
  export * from './models/http-types.js';
7
7
 
8
- // Export submodules
9
- export * from './port80/index.js';
8
+ // Export submodules (remove port80 export)
10
9
  export * from './router/index.js';
11
10
  export * from './redirects/index.js';
11
+ // REMOVED: export * from './port80/index.js';
12
12
 
13
- // Import the components we need for the namespace
14
- import { Port80Handler } from './port80/port80-handler.js';
15
- import { ChallengeResponder } from './port80/challenge-responder.js';
16
-
17
- // Convenience namespace exports
13
+ // Convenience namespace exports (no more Port80)
18
14
  export const Http = {
19
- Port80: {
20
- Handler: Port80Handler,
21
- ChallengeResponder: ChallengeResponder
22
- }
23
- };
15
+ // Only router and redirect functionality remain
16
+ };
package/ts/plugins.ts CHANGED
@@ -21,7 +21,8 @@ import * as smartdelay from '@push.rocks/smartdelay';
21
21
  import * as smartpromise from '@push.rocks/smartpromise';
22
22
  import * as smartrequest from '@push.rocks/smartrequest';
23
23
  import * as smartstring from '@push.rocks/smartstring';
24
-
24
+ import * as smartfile from '@push.rocks/smartfile';
25
+ import * as smartcrypto from '@push.rocks/smartcrypto';
25
26
  import * as smartacme from '@push.rocks/smartacme';
26
27
  import * as smartacmePlugins from '@push.rocks/smartacme/dist_ts/smartacme.plugins.js';
27
28
  import * as smartacmeHandlers from '@push.rocks/smartacme/dist_ts/handlers/index.js';
@@ -33,6 +34,8 @@ export {
33
34
  smartrequest,
34
35
  smartpromise,
35
36
  smartstring,
37
+ smartfile,
38
+ smartcrypto,
36
39
  smartacme,
37
40
  smartacmePlugins,
38
41
  smartacmeHandlers,
@@ -500,6 +500,9 @@ export class NetworkProxy implements IMetricsTracker {
500
500
  this.logger.warn('Router has no recognized configuration method');
501
501
  }
502
502
 
503
+ // Update WebSocket handler with new routes
504
+ this.webSocketHandler.setRoutes(routes);
505
+
503
506
  this.logger.info(`Route configuration updated with ${routes.length} routes and ${legacyConfigs.length} proxy configs`);
504
507
  }
505
508
 
@@ -115,6 +115,8 @@ export class WebSocketHandler {
115
115
  * Handle a new WebSocket connection
116
116
  */
117
117
  private handleWebSocketConnection(wsIncoming: IWebSocketWithHeartbeat, req: plugins.http.IncomingMessage): void {
118
+ this.logger.debug(`WebSocket connection initiated from ${req.headers.host}`);
119
+
118
120
  try {
119
121
  // Initialize heartbeat tracking
120
122
  wsIncoming.isAlive = true;
@@ -217,6 +219,8 @@ export class WebSocketHandler {
217
219
  host: selectedHost,
218
220
  port: targetPort
219
221
  };
222
+
223
+ this.logger.debug(`WebSocket destination resolved: ${selectedHost}:${targetPort}`);
220
224
  } catch (err) {
221
225
  this.logger.error(`Error evaluating function-based target for WebSocket: ${err}`);
222
226
  wsIncoming.close(1011, 'Internal server error');
@@ -240,7 +244,10 @@ export class WebSocketHandler {
240
244
  }
241
245
 
242
246
  // Build target URL with potential path rewriting
243
- const protocol = (req.socket as any).encrypted ? 'wss' : 'ws';
247
+ // Determine protocol based on the target's configuration
248
+ // For WebSocket connections, we use ws for HTTP backends and wss for HTTPS backends
249
+ const isTargetSecure = destination.port === 443;
250
+ const protocol = isTargetSecure ? 'wss' : 'ws';
244
251
  let targetPath = req.url || '/';
245
252
 
246
253
  // Apply path rewriting if configured
@@ -319,7 +326,12 @@ export class WebSocketHandler {
319
326
  }
320
327
 
321
328
  // Create outgoing WebSocket connection
329
+ this.logger.debug(`Creating WebSocket connection to ${targetUrl} with options:`, {
330
+ headers: wsOptions.headers,
331
+ protocols: wsOptions.protocols
332
+ });
322
333
  const wsOutgoing = new plugins.wsDefault(targetUrl, wsOptions);
334
+ this.logger.debug(`WebSocket instance created, waiting for connection...`);
323
335
 
324
336
  // Handle connection errors
325
337
  wsOutgoing.on('error', (err) => {
@@ -331,6 +343,7 @@ export class WebSocketHandler {
331
343
 
332
344
  // Handle outgoing connection open
333
345
  wsOutgoing.on('open', () => {
346
+ this.logger.debug(`WebSocket target connection opened to ${targetUrl}`);
334
347
  // Set up custom ping interval if configured
335
348
  let pingInterval: NodeJS.Timeout | null = null;
336
349
  if (route?.action.websocket?.pingInterval && route.action.websocket.pingInterval > 0) {
@@ -376,6 +389,7 @@ export class WebSocketHandler {
376
389
 
377
390
  // Forward incoming messages to outgoing connection
378
391
  wsIncoming.on('message', (data, isBinary) => {
392
+ this.logger.debug(`WebSocket forwarding message from client to target: ${data.toString()}`);
379
393
  if (wsOutgoing.readyState === wsOutgoing.OPEN) {
380
394
  // Check message size if limit is set
381
395
  const messageSize = getMessageSize(data);
@@ -386,13 +400,18 @@ export class WebSocketHandler {
386
400
  }
387
401
 
388
402
  wsOutgoing.send(data, { binary: isBinary });
403
+ } else {
404
+ this.logger.warn(`WebSocket target connection not open (state: ${wsOutgoing.readyState})`);
389
405
  }
390
406
  });
391
407
 
392
408
  // Forward outgoing messages to incoming connection
393
409
  wsOutgoing.on('message', (data, isBinary) => {
410
+ this.logger.debug(`WebSocket forwarding message from target to client: ${data.toString()}`);
394
411
  if (wsIncoming.readyState === wsIncoming.OPEN) {
395
412
  wsIncoming.send(data, { binary: isBinary });
413
+ } else {
414
+ this.logger.warn(`WebSocket client connection not open (state: ${wsIncoming.readyState})`);
396
415
  }
397
416
  });
398
417
 
@@ -400,7 +419,15 @@ export class WebSocketHandler {
400
419
  wsIncoming.on('close', (code, reason) => {
401
420
  this.logger.debug(`WebSocket client connection closed: ${code} ${reason}`);
402
421
  if (wsOutgoing.readyState === wsOutgoing.OPEN) {
403
- wsOutgoing.close(code, reason);
422
+ // Ensure code is a valid WebSocket close code number
423
+ const validCode = typeof code === 'number' && code >= 1000 && code <= 4999 ? code : 1000;
424
+ try {
425
+ const reasonString = reason ? toBuffer(reason).toString() : '';
426
+ wsOutgoing.close(validCode, reasonString);
427
+ } catch (err) {
428
+ this.logger.error('Error closing wsOutgoing:', err);
429
+ wsOutgoing.close(validCode);
430
+ }
404
431
  }
405
432
 
406
433
  // Clean up timers
@@ -411,7 +438,15 @@ export class WebSocketHandler {
411
438
  wsOutgoing.on('close', (code, reason) => {
412
439
  this.logger.debug(`WebSocket target connection closed: ${code} ${reason}`);
413
440
  if (wsIncoming.readyState === wsIncoming.OPEN) {
414
- wsIncoming.close(code, reason);
441
+ // Ensure code is a valid WebSocket close code number
442
+ const validCode = typeof code === 'number' && code >= 1000 && code <= 4999 ? code : 1000;
443
+ try {
444
+ const reasonString = reason ? toBuffer(reason).toString() : '';
445
+ wsIncoming.close(validCode, reasonString);
446
+ } catch (err) {
447
+ this.logger.error('Error closing wsIncoming:', err);
448
+ wsIncoming.close(validCode);
449
+ }
415
450
  }
416
451
 
417
452
  // Clean up timers
@@ -0,0 +1,86 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import type { ICertificateData } from './certificate-manager.js';
3
+
4
+ export class CertStore {
5
+ constructor(private certDir: string) {}
6
+
7
+ public async initialize(): Promise<void> {
8
+ await plugins.smartfile.fs.ensureDirSync(this.certDir);
9
+ }
10
+
11
+ public async getCertificate(routeName: string): Promise<ICertificateData | null> {
12
+ const certPath = this.getCertPath(routeName);
13
+ const metaPath = `${certPath}/meta.json`;
14
+
15
+ if (!await plugins.smartfile.fs.fileExistsSync(metaPath)) {
16
+ return null;
17
+ }
18
+
19
+ try {
20
+ const metaFile = await plugins.smartfile.SmartFile.fromFilePath(metaPath);
21
+ const meta = JSON.parse(metaFile.contents.toString());
22
+
23
+ const certFile = await plugins.smartfile.SmartFile.fromFilePath(`${certPath}/cert.pem`);
24
+ const cert = certFile.contents.toString();
25
+
26
+ const keyFile = await plugins.smartfile.SmartFile.fromFilePath(`${certPath}/key.pem`);
27
+ const key = keyFile.contents.toString();
28
+
29
+ let ca: string | undefined;
30
+ const caPath = `${certPath}/ca.pem`;
31
+ if (await plugins.smartfile.fs.fileExistsSync(caPath)) {
32
+ const caFile = await plugins.smartfile.SmartFile.fromFilePath(caPath);
33
+ ca = caFile.contents.toString();
34
+ }
35
+
36
+ return {
37
+ cert,
38
+ key,
39
+ ca,
40
+ expiryDate: new Date(meta.expiryDate),
41
+ issueDate: new Date(meta.issueDate)
42
+ };
43
+ } catch (error) {
44
+ console.error(`Failed to load certificate for ${routeName}: ${error}`);
45
+ return null;
46
+ }
47
+ }
48
+
49
+ public async saveCertificate(
50
+ routeName: string,
51
+ certData: ICertificateData
52
+ ): Promise<void> {
53
+ const certPath = this.getCertPath(routeName);
54
+ await plugins.smartfile.fs.ensureDirSync(certPath);
55
+
56
+ // Save certificate files
57
+ await plugins.smartfile.memory.toFs(certData.cert, `${certPath}/cert.pem`);
58
+ await plugins.smartfile.memory.toFs(certData.key, `${certPath}/key.pem`);
59
+
60
+ if (certData.ca) {
61
+ await plugins.smartfile.memory.toFs(certData.ca, `${certPath}/ca.pem`);
62
+ }
63
+
64
+ // Save metadata
65
+ const meta = {
66
+ expiryDate: certData.expiryDate.toISOString(),
67
+ issueDate: certData.issueDate.toISOString(),
68
+ savedAt: new Date().toISOString()
69
+ };
70
+
71
+ await plugins.smartfile.memory.toFs(JSON.stringify(meta, null, 2), `${certPath}/meta.json`);
72
+ }
73
+
74
+ public async deleteCertificate(routeName: string): Promise<void> {
75
+ const certPath = this.getCertPath(routeName);
76
+ if (await plugins.smartfile.fs.fileExistsSync(certPath)) {
77
+ await plugins.smartfile.fs.removeManySync([certPath]);
78
+ }
79
+ }
80
+
81
+ private getCertPath(routeName: string): string {
82
+ // Sanitize route name for filesystem
83
+ const safeName = routeName.replace(/[^a-zA-Z0-9-_]/g, '_');
84
+ return `${this.certDir}/${safeName}`;
85
+ }
86
+ }