@push.rocks/smartproxy 21.1.6 → 22.4.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 (103) hide show
  1. package/changelog.md +89 -0
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/core/utils/shared-security-manager.d.ts +17 -0
  4. package/dist_ts/core/utils/shared-security-manager.js +66 -1
  5. package/dist_ts/proxies/http-proxy/default-certificates.d.ts +54 -0
  6. package/dist_ts/proxies/http-proxy/default-certificates.js +127 -0
  7. package/dist_ts/proxies/http-proxy/http-proxy.d.ts +1 -1
  8. package/dist_ts/proxies/http-proxy/http-proxy.js +9 -14
  9. package/dist_ts/proxies/http-proxy/index.d.ts +5 -1
  10. package/dist_ts/proxies/http-proxy/index.js +6 -2
  11. package/dist_ts/proxies/http-proxy/security-manager.d.ts +4 -12
  12. package/dist_ts/proxies/http-proxy/security-manager.js +66 -99
  13. package/dist_ts/proxies/nftables-proxy/index.d.ts +1 -0
  14. package/dist_ts/proxies/nftables-proxy/index.js +2 -1
  15. package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +4 -26
  16. package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +84 -236
  17. package/dist_ts/proxies/nftables-proxy/utils/index.d.ts +9 -0
  18. package/dist_ts/proxies/nftables-proxy/utils/index.js +12 -0
  19. package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.d.ts +66 -0
  20. package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.js +131 -0
  21. package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.d.ts +39 -0
  22. package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.js +112 -0
  23. package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.d.ts +59 -0
  24. package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.js +130 -0
  25. package/dist_ts/proxies/smart-proxy/certificate-manager.js +4 -3
  26. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +13 -2
  27. package/dist_ts/proxies/smart-proxy/connection-manager.js +16 -6
  28. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +35 -10
  29. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +0 -1
  30. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +17 -0
  31. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +72 -9
  32. package/dist_ts/proxies/smart-proxy/security-manager.d.ts +14 -12
  33. package/dist_ts/proxies/smart-proxy/security-manager.js +80 -74
  34. package/dist_ts/proxies/smart-proxy/smart-proxy.js +1 -2
  35. package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +2 -9
  36. package/dist_ts/proxies/smart-proxy/tls-manager.js +3 -26
  37. package/dist_ts/proxies/smart-proxy/utils/index.d.ts +1 -1
  38. package/dist_ts/proxies/smart-proxy/utils/index.js +3 -4
  39. package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.d.ts +49 -0
  40. package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.js +108 -0
  41. package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.d.ts +57 -0
  42. package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.js +89 -0
  43. package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.d.ts +17 -0
  44. package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.js +32 -0
  45. package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.d.ts +68 -0
  46. package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.js +117 -0
  47. package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.d.ts +17 -0
  48. package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.js +27 -0
  49. package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.d.ts +63 -0
  50. package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.js +105 -0
  51. package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.d.ts +83 -0
  52. package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.js +126 -0
  53. package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.d.ts +47 -0
  54. package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.js +66 -0
  55. package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.d.ts +70 -0
  56. package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.js +287 -0
  57. package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.d.ts +46 -0
  58. package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.js +67 -0
  59. package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +4 -457
  60. package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +6 -950
  61. package/dist_ts/proxies/smart-proxy/utils/route-utils.js +2 -2
  62. package/dist_ts/proxies/smart-proxy/utils/route-validator.d.ts +67 -1
  63. package/dist_ts/proxies/smart-proxy/utils/route-validator.js +266 -6
  64. package/npmextra.json +12 -6
  65. package/package.json +34 -24
  66. package/readme.hints.md +184 -1
  67. package/readme.md +235 -172
  68. package/ts/00_commitinfo_data.ts +1 -1
  69. package/ts/core/utils/shared-security-manager.ts +98 -13
  70. package/ts/proxies/http-proxy/default-certificates.ts +150 -0
  71. package/ts/proxies/http-proxy/http-proxy.ts +9 -15
  72. package/ts/proxies/http-proxy/index.ts +6 -1
  73. package/ts/proxies/http-proxy/security-manager.ts +141 -161
  74. package/ts/proxies/nftables-proxy/index.ts +1 -0
  75. package/ts/proxies/nftables-proxy/nftables-proxy.ts +116 -290
  76. package/ts/proxies/nftables-proxy/utils/index.ts +38 -0
  77. package/ts/proxies/nftables-proxy/utils/nft-command-executor.ts +162 -0
  78. package/ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.ts +125 -0
  79. package/ts/proxies/nftables-proxy/utils/nft-rule-validator.ts +156 -0
  80. package/ts/proxies/smart-proxy/certificate-manager.ts +3 -2
  81. package/ts/proxies/smart-proxy/connection-manager.ts +21 -8
  82. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +39 -13
  83. package/ts/proxies/smart-proxy/models/interfaces.ts +0 -1
  84. package/ts/proxies/smart-proxy/route-connection-handler.ts +88 -16
  85. package/ts/proxies/smart-proxy/security-manager.ts +98 -86
  86. package/ts/proxies/smart-proxy/smart-proxy.ts +0 -2
  87. package/ts/proxies/smart-proxy/tls-manager.ts +1 -37
  88. package/ts/proxies/smart-proxy/utils/index.ts +3 -5
  89. package/ts/proxies/smart-proxy/utils/route-helpers/api-helpers.ts +144 -0
  90. package/ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.ts +124 -0
  91. package/ts/proxies/smart-proxy/utils/route-helpers/http-helpers.ts +40 -0
  92. package/ts/proxies/smart-proxy/utils/route-helpers/https-helpers.ts +163 -0
  93. package/ts/proxies/smart-proxy/utils/route-helpers/index.ts +62 -0
  94. package/ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.ts +154 -0
  95. package/ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.ts +202 -0
  96. package/ts/proxies/smart-proxy/utils/route-helpers/security-helpers.ts +96 -0
  97. package/ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.ts +337 -0
  98. package/ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.ts +98 -0
  99. package/ts/proxies/smart-proxy/utils/route-helpers.ts +5 -1302
  100. package/ts/proxies/smart-proxy/utils/route-utils.ts +1 -1
  101. package/ts/proxies/smart-proxy/utils/route-validator.ts +289 -7
  102. package/ts/proxies/http-proxy/certificate-manager.ts +0 -244
  103. package/ts/proxies/smart-proxy/utils/route-validators.ts +0 -283
@@ -1,955 +1,11 @@
1
1
  /**
2
2
  * Route Helper Functions
3
3
  *
4
- * This file provides utility functions for creating route configurations for common scenarios.
5
- * These functions aim to simplify the creation of route configurations for typical use cases.
4
+ * This file re-exports all route helper functions for backwards compatibility.
5
+ * The actual implementations have been split into focused modules in the route-helpers/ directory.
6
6
  *
7
- * This module includes helper functions for creating:
8
- * - HTTP routes (createHttpRoute)
9
- * - HTTPS routes with TLS termination (createHttpsTerminateRoute)
10
- * - HTTP to HTTPS redirects (createHttpToHttpsRedirect)
11
- * - HTTPS passthrough routes (createHttpsPassthroughRoute)
12
- * - Complete HTTPS servers with redirects (createCompleteHttpsServer)
13
- * - Load balancer routes (createLoadBalancerRoute)
14
- * - API routes (createApiRoute)
15
- * - WebSocket routes (createWebSocketRoute)
16
- * - Port mapping routes (createPortMappingRoute, createOffsetPortMappingRoute)
17
- * - Dynamic routing (createDynamicRoute, createSmartLoadBalancer)
18
- * - NFTables routes (createNfTablesRoute, createNfTablesTerminateRoute)
7
+ * @see ./route-helpers/index.ts for the modular exports
19
8
  */
20
- import * as plugins from '../../../plugins.js';
21
- import { mergeRouteConfigs } from './route-utils.js';
22
- import { ProtocolDetector, HttpDetector } from '../../../detection/index.js';
23
- import { createSocketTracker } from '../../../core/utils/socket-tracker.js';
24
- /**
25
- * Create an HTTP-only route configuration
26
- * @param domains Domain(s) to match
27
- * @param target Target host and port
28
- * @param options Additional route options
29
- * @returns Route configuration object
30
- */
31
- export function createHttpRoute(domains, target, options = {}) {
32
- // Create route match
33
- const match = {
34
- ports: options.match?.ports || 80,
35
- domains
36
- };
37
- // Create route action
38
- const action = {
39
- type: 'forward',
40
- targets: [target]
41
- };
42
- // Create the route config
43
- return {
44
- match,
45
- action,
46
- name: options.name || `HTTP Route for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
47
- ...options
48
- };
49
- }
50
- /**
51
- * Create an HTTPS route with TLS termination (including HTTP redirect to HTTPS)
52
- * @param domains Domain(s) to match
53
- * @param target Target host and port
54
- * @param options Additional route options
55
- * @returns Route configuration object
56
- */
57
- export function createHttpsTerminateRoute(domains, target, options = {}) {
58
- // Create route match
59
- const match = {
60
- ports: options.httpsPort || 443,
61
- domains
62
- };
63
- // Create route action
64
- const action = {
65
- type: 'forward',
66
- targets: [target],
67
- tls: {
68
- mode: options.reencrypt ? 'terminate-and-reencrypt' : 'terminate',
69
- certificate: options.certificate || 'auto'
70
- }
71
- };
72
- // Create the route config
73
- return {
74
- match,
75
- action,
76
- name: options.name || `HTTPS Route for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
77
- ...options
78
- };
79
- }
80
- /**
81
- * Create an HTTP to HTTPS redirect route
82
- * @param domains Domain(s) to match
83
- * @param httpsPort HTTPS port to redirect to (default: 443)
84
- * @param options Additional route options
85
- * @returns Route configuration object
86
- */
87
- export function createHttpToHttpsRedirect(domains, httpsPort = 443, options = {}) {
88
- // Create route match
89
- const match = {
90
- ports: options.match?.ports || 80,
91
- domains
92
- };
93
- // Create route action
94
- const action = {
95
- type: 'socket-handler',
96
- socketHandler: SocketHandlers.httpRedirect(`https://{domain}:${httpsPort}{path}`, 301)
97
- };
98
- // Create the route config
99
- return {
100
- match,
101
- action,
102
- name: options.name || `HTTP to HTTPS Redirect for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
103
- ...options
104
- };
105
- }
106
- /**
107
- * Create an HTTPS passthrough route (SNI-based forwarding without TLS termination)
108
- * @param domains Domain(s) to match
109
- * @param target Target host and port
110
- * @param options Additional route options
111
- * @returns Route configuration object
112
- */
113
- export function createHttpsPassthroughRoute(domains, target, options = {}) {
114
- // Create route match
115
- const match = {
116
- ports: options.match?.ports || 443,
117
- domains
118
- };
119
- // Create route action
120
- const action = {
121
- type: 'forward',
122
- targets: [target],
123
- tls: {
124
- mode: 'passthrough'
125
- }
126
- };
127
- // Create the route config
128
- return {
129
- match,
130
- action,
131
- name: options.name || `HTTPS Passthrough for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
132
- ...options
133
- };
134
- }
135
- /**
136
- * Create a complete HTTPS server with HTTP to HTTPS redirects
137
- * @param domains Domain(s) to match
138
- * @param target Target host and port
139
- * @param options Additional configuration options
140
- * @returns Array of two route configurations (HTTPS and HTTP redirect)
141
- */
142
- export function createCompleteHttpsServer(domains, target, options = {}) {
143
- // Create the HTTPS route
144
- const httpsRoute = createHttpsTerminateRoute(domains, target, options);
145
- // Create the HTTP redirect route
146
- const httpRedirectRoute = createHttpToHttpsRedirect(domains,
147
- // Extract the HTTPS port from the HTTPS route - ensure it's a number
148
- typeof options.httpsPort === 'number' ? options.httpsPort :
149
- Array.isArray(options.httpsPort) ? options.httpsPort[0] : 443, {
150
- // Set the HTTP port
151
- match: {
152
- ports: options.httpPort || 80,
153
- domains
154
- },
155
- name: `HTTP to HTTPS Redirect for ${Array.isArray(domains) ? domains.join(', ') : domains}`
156
- });
157
- return [httpsRoute, httpRedirectRoute];
158
- }
159
- /**
160
- * Create a load balancer route (round-robin between multiple backend hosts)
161
- * @param domains Domain(s) to match
162
- * @param backendsOrHosts Array of backend servers OR array of host strings (legacy)
163
- * @param portOrOptions Port number (legacy) OR options object
164
- * @param options Additional route options (legacy)
165
- * @returns Route configuration object
166
- */
167
- export function createLoadBalancerRoute(domains, backendsOrHosts, portOrOptions, options) {
168
- // Handle legacy signature: (domains, hosts[], port, options)
169
- let backends;
170
- let finalOptions;
171
- if (Array.isArray(backendsOrHosts) && backendsOrHosts.length > 0 && typeof backendsOrHosts[0] === 'string') {
172
- // Legacy signature
173
- const hosts = backendsOrHosts;
174
- const port = portOrOptions;
175
- backends = hosts.map(host => ({ host, port }));
176
- finalOptions = options || {};
177
- }
178
- else {
179
- // New signature
180
- backends = backendsOrHosts;
181
- finalOptions = portOrOptions || {};
182
- }
183
- // Extract hosts and ensure all backends use the same port
184
- const port = backends[0].port;
185
- const hosts = backends.map(backend => backend.host);
186
- // Create route match
187
- const match = {
188
- ports: finalOptions.match?.ports || (finalOptions.tls || finalOptions.useTls ? 443 : 80),
189
- domains
190
- };
191
- // Create route target
192
- const target = {
193
- host: hosts,
194
- port
195
- };
196
- // Create route action
197
- const action = {
198
- type: 'forward',
199
- targets: [target]
200
- };
201
- // Add TLS configuration if provided
202
- if (finalOptions.tls || finalOptions.useTls) {
203
- action.tls = {
204
- mode: finalOptions.tls?.mode || 'terminate',
205
- certificate: finalOptions.tls?.certificate || finalOptions.certificate || 'auto'
206
- };
207
- }
208
- // Add load balancing options
209
- if (finalOptions.algorithm || finalOptions.healthCheck) {
210
- action.loadBalancing = {
211
- algorithm: finalOptions.algorithm || 'round-robin',
212
- healthCheck: finalOptions.healthCheck
213
- };
214
- }
215
- // Create the route config
216
- return {
217
- match,
218
- action,
219
- name: finalOptions.name || `Load Balancer for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
220
- ...finalOptions
221
- };
222
- }
223
- /**
224
- * Create an API route configuration
225
- * @param domains Domain(s) to match
226
- * @param apiPath API base path (e.g., "/api")
227
- * @param target Target host and port
228
- * @param options Additional route options
229
- * @returns Route configuration object
230
- */
231
- export function createApiRoute(domains, apiPath, target, options = {}) {
232
- // Normalize API path
233
- const normalizedPath = apiPath.startsWith('/') ? apiPath : `/${apiPath}`;
234
- const pathWithWildcard = normalizedPath.endsWith('/')
235
- ? `${normalizedPath}*`
236
- : `${normalizedPath}/*`;
237
- // Create route match
238
- const match = {
239
- ports: options.useTls
240
- ? (options.httpsPort || 443)
241
- : (options.httpPort || 80),
242
- domains,
243
- path: pathWithWildcard
244
- };
245
- // Create route action
246
- const action = {
247
- type: 'forward',
248
- targets: [target]
249
- };
250
- // Add TLS configuration if using HTTPS
251
- if (options.useTls) {
252
- action.tls = {
253
- mode: 'terminate',
254
- certificate: options.certificate || 'auto'
255
- };
256
- }
257
- // Add CORS headers if requested
258
- const headers = {};
259
- if (options.addCorsHeaders) {
260
- headers.response = {
261
- 'Access-Control-Allow-Origin': '*',
262
- 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
263
- 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
264
- 'Access-Control-Max-Age': '86400'
265
- };
266
- }
267
- // Create the route config
268
- return {
269
- match,
270
- action,
271
- headers: Object.keys(headers).length > 0 ? headers : undefined,
272
- name: options.name || `API Route ${normalizedPath} for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
273
- priority: options.priority || 100, // Higher priority for specific path matches
274
- ...options
275
- };
276
- }
277
- /**
278
- * Create a WebSocket route configuration
279
- * @param domains Domain(s) to match
280
- * @param targetOrPath Target server OR WebSocket path (legacy)
281
- * @param targetOrOptions Target server (legacy) OR options
282
- * @param options Additional route options (legacy)
283
- * @returns Route configuration object
284
- */
285
- export function createWebSocketRoute(domains, targetOrPath, targetOrOptions, options) {
286
- // Handle different signatures
287
- let target;
288
- let wsPath;
289
- let finalOptions;
290
- if (typeof targetOrPath === 'string') {
291
- // Legacy signature: (domains, path, target, options)
292
- wsPath = targetOrPath;
293
- target = targetOrOptions;
294
- finalOptions = options || {};
295
- }
296
- else {
297
- // New signature: (domains, target, options)
298
- target = targetOrPath;
299
- finalOptions = targetOrOptions || {};
300
- wsPath = finalOptions.path || '/ws';
301
- }
302
- // Normalize WebSocket path
303
- const normalizedPath = wsPath.startsWith('/') ? wsPath : `/${wsPath}`;
304
- // Create route match
305
- const match = {
306
- ports: finalOptions.useTls
307
- ? (finalOptions.httpsPort || 443)
308
- : (finalOptions.httpPort || 80),
309
- domains,
310
- path: normalizedPath
311
- };
312
- // Create route action
313
- const action = {
314
- type: 'forward',
315
- targets: [target],
316
- websocket: {
317
- enabled: true,
318
- pingInterval: finalOptions.pingInterval || 30000, // 30 seconds
319
- pingTimeout: finalOptions.pingTimeout || 5000 // 5 seconds
320
- }
321
- };
322
- // Add TLS configuration if using HTTPS
323
- if (finalOptions.useTls) {
324
- action.tls = {
325
- mode: 'terminate',
326
- certificate: finalOptions.certificate || 'auto'
327
- };
328
- }
329
- // Create the route config
330
- return {
331
- match,
332
- action,
333
- name: finalOptions.name || `WebSocket Route ${normalizedPath} for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
334
- priority: finalOptions.priority || 100, // Higher priority for WebSocket routes
335
- ...finalOptions
336
- };
337
- }
338
- /**
339
- * Create a helper function that applies a port offset
340
- * @param offset The offset to apply to the matched port
341
- * @returns A function that adds the offset to the matched port
342
- */
343
- export function createPortOffset(offset) {
344
- return (context) => context.port + offset;
345
- }
346
- /**
347
- * Create a port mapping route with context-based port function
348
- * @param options Port mapping route options
349
- * @returns Route configuration object
350
- */
351
- export function createPortMappingRoute(options) {
352
- // Create route match
353
- const match = {
354
- ports: options.sourcePortRange,
355
- domains: options.domains
356
- };
357
- // Create route action
358
- const action = {
359
- type: 'forward',
360
- targets: [{
361
- host: options.targetHost,
362
- port: options.portMapper
363
- }]
364
- };
365
- // Create the route config
366
- return {
367
- match,
368
- action,
369
- name: options.name || `Port Mapping Route for ${options.domains || 'all domains'}`,
370
- priority: options.priority,
371
- ...options
372
- };
373
- }
374
- /**
375
- * Create a simple offset port mapping route
376
- * @param options Offset port mapping route options
377
- * @returns Route configuration object
378
- */
379
- export function createOffsetPortMappingRoute(options) {
380
- return createPortMappingRoute({
381
- sourcePortRange: options.ports,
382
- targetHost: options.targetHost,
383
- portMapper: (context) => context.port + options.offset,
384
- name: options.name || `Offset Mapping (${options.offset > 0 ? '+' : ''}${options.offset}) for ${options.domains || 'all domains'}`,
385
- domains: options.domains,
386
- priority: options.priority,
387
- ...options
388
- });
389
- }
390
- /**
391
- * Create a dynamic route with context-based host and port mapping
392
- * @param options Dynamic route options
393
- * @returns Route configuration object
394
- */
395
- export function createDynamicRoute(options) {
396
- // Create route match
397
- const match = {
398
- ports: options.ports,
399
- domains: options.domains,
400
- path: options.path,
401
- clientIp: options.clientIp
402
- };
403
- // Create route action
404
- const action = {
405
- type: 'forward',
406
- targets: [{
407
- host: options.targetHost,
408
- port: options.portMapper
409
- }]
410
- };
411
- // Create the route config
412
- return {
413
- match,
414
- action,
415
- name: options.name || `Dynamic Route for ${options.domains || 'all domains'}`,
416
- priority: options.priority,
417
- ...options
418
- };
419
- }
420
- /**
421
- * Create a smart load balancer with dynamic domain-based backend selection
422
- * @param options Smart load balancer options
423
- * @returns Route configuration object
424
- */
425
- export function createSmartLoadBalancer(options) {
426
- // Extract all domain keys to create the match criteria
427
- const domains = Object.keys(options.domainTargets);
428
- // Create the smart host selector function
429
- const hostSelector = (context) => {
430
- const domain = context.domain || '';
431
- return options.domainTargets[domain] || options.defaultTarget || 'localhost';
432
- };
433
- // Create route match
434
- const match = {
435
- ports: options.ports,
436
- domains
437
- };
438
- // Create route action
439
- const action = {
440
- type: 'forward',
441
- targets: [{
442
- host: hostSelector,
443
- port: options.portMapper
444
- }]
445
- };
446
- // Create the route config
447
- return {
448
- match,
449
- action,
450
- name: options.name || `Smart Load Balancer for ${domains.join(', ')}`,
451
- priority: options.priority,
452
- ...options
453
- };
454
- }
455
- /**
456
- * Create an NFTables-based route for high-performance packet forwarding
457
- * @param nameOrDomains Name or domain(s) to match
458
- * @param target Target host and port
459
- * @param options Additional route options
460
- * @returns Route configuration object
461
- */
462
- export function createNfTablesRoute(nameOrDomains, target, options = {}) {
463
- // Determine if this is a name or domain
464
- let name;
465
- let domains;
466
- if (Array.isArray(nameOrDomains) || (typeof nameOrDomains === 'string' && nameOrDomains.includes('.'))) {
467
- domains = nameOrDomains;
468
- name = Array.isArray(nameOrDomains) ? nameOrDomains[0] : nameOrDomains;
469
- }
470
- else {
471
- name = nameOrDomains;
472
- domains = undefined; // No domains
473
- }
474
- // Create route match
475
- const match = {
476
- domains,
477
- ports: options.ports || 80
478
- };
479
- // Create route action
480
- const action = {
481
- type: 'forward',
482
- targets: [{
483
- host: target.host,
484
- port: target.port
485
- }],
486
- forwardingEngine: 'nftables',
487
- nftables: {
488
- protocol: options.protocol || 'tcp',
489
- preserveSourceIP: options.preserveSourceIP,
490
- maxRate: options.maxRate,
491
- priority: options.priority,
492
- tableName: options.tableName,
493
- useIPSets: options.useIPSets,
494
- useAdvancedNAT: options.useAdvancedNAT
495
- }
496
- };
497
- // Add TLS options if needed
498
- if (options.useTls) {
499
- action.tls = {
500
- mode: 'passthrough'
501
- };
502
- }
503
- // Create the route config
504
- const routeConfig = {
505
- name,
506
- match,
507
- action
508
- };
509
- // Add security if allowed or blocked IPs are specified
510
- if (options.ipAllowList?.length || options.ipBlockList?.length) {
511
- routeConfig.security = {
512
- ipAllowList: options.ipAllowList,
513
- ipBlockList: options.ipBlockList
514
- };
515
- }
516
- return routeConfig;
517
- }
518
- /**
519
- * Create an NFTables-based TLS termination route
520
- * @param nameOrDomains Name or domain(s) to match
521
- * @param target Target host and port
522
- * @param options Additional route options
523
- * @returns Route configuration object
524
- */
525
- export function createNfTablesTerminateRoute(nameOrDomains, target, options = {}) {
526
- // Create basic NFTables route
527
- const route = createNfTablesRoute(nameOrDomains, target, {
528
- ...options,
529
- ports: options.ports || 443,
530
- useTls: false
531
- });
532
- // Set TLS termination
533
- route.action.tls = {
534
- mode: 'terminate',
535
- certificate: options.certificate || 'auto'
536
- };
537
- return route;
538
- }
539
- /**
540
- * Create a complete NFTables-based HTTPS setup with HTTP redirect
541
- * @param nameOrDomains Name or domain(s) to match
542
- * @param target Target host and port
543
- * @param options Additional route options
544
- * @returns Array of two route configurations (HTTPS and HTTP redirect)
545
- */
546
- export function createCompleteNfTablesHttpsServer(nameOrDomains, target, options = {}) {
547
- // Create the HTTPS route using NFTables
548
- const httpsRoute = createNfTablesTerminateRoute(nameOrDomains, target, {
549
- ...options,
550
- ports: options.httpsPort || 443
551
- });
552
- // Determine the domain(s) for HTTP redirect
553
- const domains = typeof nameOrDomains === 'string' && !nameOrDomains.includes('.')
554
- ? undefined
555
- : nameOrDomains;
556
- // Extract the HTTPS port for the redirect destination
557
- const httpsPort = typeof options.httpsPort === 'number'
558
- ? options.httpsPort
559
- : Array.isArray(options.httpsPort) && typeof options.httpsPort[0] === 'number'
560
- ? options.httpsPort[0]
561
- : 443;
562
- // Create the HTTP redirect route (this uses standard forwarding, not NFTables)
563
- const httpRedirectRoute = createHttpToHttpsRedirect(domains, // Type cast needed since domains can be undefined now
564
- httpsPort, {
565
- match: {
566
- ports: options.httpPort || 80,
567
- domains: domains // Type cast needed since domains can be undefined now
568
- },
569
- name: `HTTP to HTTPS Redirect for ${Array.isArray(domains) ? domains.join(', ') : domains || 'all domains'}`
570
- });
571
- return [httpsRoute, httpRedirectRoute];
572
- }
573
- /**
574
- * Create a socket handler route configuration
575
- * @param domains Domain(s) to match
576
- * @param ports Port(s) to listen on
577
- * @param handler Socket handler function
578
- * @param options Additional route options
579
- * @returns Route configuration object
580
- */
581
- export function createSocketHandlerRoute(domains, ports, handler, options = {}) {
582
- return {
583
- name: options.name || 'socket-handler-route',
584
- priority: options.priority !== undefined ? options.priority : 50,
585
- match: {
586
- domains,
587
- ports,
588
- ...(options.path && { path: options.path })
589
- },
590
- action: {
591
- type: 'socket-handler',
592
- socketHandler: handler
593
- }
594
- };
595
- }
596
- /**
597
- * Pre-built socket handlers for common use cases
598
- */
599
- export const SocketHandlers = {
600
- /**
601
- * Simple echo server handler
602
- */
603
- echo: (socket, context) => {
604
- socket.write('ECHO SERVER READY\n');
605
- socket.on('data', data => socket.write(data));
606
- },
607
- /**
608
- * TCP proxy handler
609
- */
610
- proxy: (targetHost, targetPort) => (socket, context) => {
611
- const target = plugins.net.connect(targetPort, targetHost);
612
- socket.pipe(target);
613
- target.pipe(socket);
614
- socket.on('close', () => target.destroy());
615
- target.on('close', () => socket.destroy());
616
- target.on('error', (err) => {
617
- console.error('Proxy target error:', err);
618
- socket.destroy();
619
- });
620
- },
621
- /**
622
- * Line-based protocol handler
623
- */
624
- lineProtocol: (handler) => (socket, context) => {
625
- let buffer = '';
626
- socket.on('data', (data) => {
627
- buffer += data.toString();
628
- const lines = buffer.split('\n');
629
- buffer = lines.pop() || '';
630
- lines.forEach(line => {
631
- if (line.trim()) {
632
- handler(line.trim(), socket);
633
- }
634
- });
635
- });
636
- },
637
- /**
638
- * Simple HTTP response handler (for testing)
639
- */
640
- httpResponse: (statusCode, body) => (socket, context) => {
641
- const response = [
642
- `HTTP/1.1 ${statusCode} ${statusCode === 200 ? 'OK' : 'Error'}`,
643
- 'Content-Type: text/plain',
644
- `Content-Length: ${body.length}`,
645
- 'Connection: close',
646
- '',
647
- body
648
- ].join('\r\n');
649
- socket.write(response);
650
- socket.end();
651
- },
652
- /**
653
- * Block connection immediately
654
- */
655
- block: (message) => (socket, context) => {
656
- const finalMessage = message || `Connection blocked from ${context.clientIp}`;
657
- if (finalMessage) {
658
- socket.write(finalMessage);
659
- }
660
- socket.end();
661
- },
662
- /**
663
- * HTTP block response
664
- */
665
- httpBlock: (statusCode = 403, message) => (socket, context) => {
666
- const defaultMessage = `Access forbidden for ${context.domain || context.clientIp}`;
667
- const finalMessage = message || defaultMessage;
668
- const response = [
669
- `HTTP/1.1 ${statusCode} ${finalMessage}`,
670
- 'Content-Type: text/plain',
671
- `Content-Length: ${finalMessage.length}`,
672
- 'Connection: close',
673
- '',
674
- finalMessage
675
- ].join('\r\n');
676
- socket.write(response);
677
- socket.end();
678
- },
679
- /**
680
- * HTTP redirect handler
681
- * Now uses the centralized detection module for HTTP parsing
682
- */
683
- httpRedirect: (locationTemplate, statusCode = 301) => (socket, context) => {
684
- const tracker = createSocketTracker(socket);
685
- const connectionId = ProtocolDetector.createConnectionId({
686
- socketId: context.connectionId || `${Date.now()}-${Math.random()}`
687
- });
688
- const handleData = async (data) => {
689
- // Use detection module for parsing
690
- const detectionResult = await ProtocolDetector.detectWithConnectionTracking(data, connectionId, { extractFullHeaders: false } // We only need method and path
691
- );
692
- if (detectionResult.protocol === 'http' && detectionResult.connectionInfo.path) {
693
- const method = detectionResult.connectionInfo.method || 'GET';
694
- const path = detectionResult.connectionInfo.path || '/';
695
- const domain = context.domain || 'localhost';
696
- const port = context.port;
697
- let finalLocation = locationTemplate
698
- .replace('{domain}', domain)
699
- .replace('{port}', String(port))
700
- .replace('{path}', path)
701
- .replace('{clientIp}', context.clientIp);
702
- const message = `Redirecting to ${finalLocation}`;
703
- const response = [
704
- `HTTP/1.1 ${statusCode} ${statusCode === 301 ? 'Moved Permanently' : 'Found'}`,
705
- `Location: ${finalLocation}`,
706
- 'Content-Type: text/plain',
707
- `Content-Length: ${message.length}`,
708
- 'Connection: close',
709
- '',
710
- message
711
- ].join('\r\n');
712
- socket.write(response);
713
- }
714
- else {
715
- // Not a valid HTTP request, close connection
716
- socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
717
- }
718
- socket.end();
719
- // Clean up detection state
720
- ProtocolDetector.cleanupConnections();
721
- // Clean up all tracked resources
722
- tracker.cleanup();
723
- };
724
- // Use tracker to manage the listener
725
- socket.once('data', handleData);
726
- tracker.addListener('error', (err) => {
727
- tracker.safeDestroy(err);
728
- });
729
- tracker.addListener('close', () => {
730
- tracker.cleanup();
731
- });
732
- },
733
- /**
734
- * HTTP server handler for ACME challenges and other HTTP needs
735
- * Now uses the centralized detection module for HTTP parsing
736
- */
737
- httpServer: (handler) => (socket, context) => {
738
- const tracker = createSocketTracker(socket);
739
- let requestParsed = false;
740
- let responseTimer = null;
741
- const connectionId = ProtocolDetector.createConnectionId({
742
- socketId: context.connectionId || `${Date.now()}-${Math.random()}`
743
- });
744
- const processData = async (data) => {
745
- if (requestParsed)
746
- return; // Only handle the first request
747
- // Use HttpDetector for parsing
748
- const detectionResult = await ProtocolDetector.detectWithConnectionTracking(data, connectionId, { extractFullHeaders: true });
749
- if (detectionResult.protocol !== 'http' || !detectionResult.isComplete) {
750
- // Not a complete HTTP request yet
751
- return;
752
- }
753
- requestParsed = true;
754
- // Remove data listener after parsing request
755
- socket.removeListener('data', processData);
756
- const connInfo = detectionResult.connectionInfo;
757
- // Create request object from detection result
758
- const req = {
759
- method: connInfo.method || 'GET',
760
- url: connInfo.path || '/',
761
- headers: connInfo.headers || {},
762
- body: detectionResult.remainingBuffer?.toString() || ''
763
- };
764
- // Create response object
765
- let statusCode = 200;
766
- const responseHeaders = {};
767
- let ended = false;
768
- const res = {
769
- status: (code) => {
770
- statusCode = code;
771
- },
772
- header: (name, value) => {
773
- responseHeaders[name] = value;
774
- },
775
- send: (data) => {
776
- if (ended)
777
- return;
778
- ended = true;
779
- // Clear response timer since we're sending now
780
- if (responseTimer) {
781
- clearTimeout(responseTimer);
782
- responseTimer = null;
783
- }
784
- if (!responseHeaders['content-type']) {
785
- responseHeaders['content-type'] = 'text/plain';
786
- }
787
- responseHeaders['content-length'] = String(data.length);
788
- responseHeaders['connection'] = 'close';
789
- const statusText = statusCode === 200 ? 'OK' :
790
- statusCode === 404 ? 'Not Found' :
791
- statusCode === 500 ? 'Internal Server Error' : 'Response';
792
- let response = `HTTP/1.1 ${statusCode} ${statusText}\r\n`;
793
- for (const [name, value] of Object.entries(responseHeaders)) {
794
- response += `${name}: ${value}\r\n`;
795
- }
796
- response += '\r\n';
797
- response += data;
798
- socket.write(response);
799
- socket.end();
800
- },
801
- end: () => {
802
- if (ended)
803
- return;
804
- ended = true;
805
- socket.write('HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n');
806
- socket.end();
807
- }
808
- };
809
- try {
810
- handler(req, res);
811
- // Ensure response is sent even if handler doesn't call send()
812
- responseTimer = setTimeout(() => {
813
- if (!ended) {
814
- res.send('');
815
- }
816
- responseTimer = null;
817
- }, 1000);
818
- // Track and unref the timer
819
- tracker.addTimer(responseTimer);
820
- }
821
- catch (error) {
822
- if (!ended) {
823
- res.status(500);
824
- res.send('Internal Server Error');
825
- }
826
- // Use safeDestroy for error cases
827
- tracker.safeDestroy(error instanceof Error ? error : new Error('Handler error'));
828
- }
829
- };
830
- // Use tracker to manage listeners
831
- tracker.addListener('data', processData);
832
- tracker.addListener('error', (err) => {
833
- if (!requestParsed) {
834
- tracker.safeDestroy(err);
835
- }
836
- });
837
- tracker.addListener('close', () => {
838
- // Cleanup is handled by tracker
839
- // Clear any pending response timer
840
- if (responseTimer) {
841
- clearTimeout(responseTimer);
842
- responseTimer = null;
843
- }
844
- // Clean up detection state
845
- ProtocolDetector.cleanupConnections();
846
- // Clean up all tracked resources
847
- tracker.cleanup();
848
- });
849
- }
850
- };
851
- /**
852
- * Create an API Gateway route pattern
853
- * @param domains Domain(s) to match
854
- * @param apiBasePath Base path for API endpoints (e.g., '/api')
855
- * @param target Target host and port
856
- * @param options Additional route options
857
- * @returns API route configuration
858
- */
859
- export function createApiGatewayRoute(domains, apiBasePath, target, options = {}) {
860
- // Normalize apiBasePath to ensure it starts with / and doesn't end with /
861
- const normalizedPath = apiBasePath.startsWith('/')
862
- ? apiBasePath
863
- : `/${apiBasePath}`;
864
- // Add wildcard to path to match all API endpoints
865
- const apiPath = normalizedPath.endsWith('/')
866
- ? `${normalizedPath}*`
867
- : `${normalizedPath}/*`;
868
- // Create base route
869
- const baseRoute = options.useTls
870
- ? createHttpsTerminateRoute(domains, target, {
871
- certificate: options.certificate || 'auto'
872
- })
873
- : createHttpRoute(domains, target);
874
- // Add API-specific configurations
875
- const apiRoute = {
876
- match: {
877
- ...baseRoute.match,
878
- path: apiPath
879
- },
880
- name: options.name || `API Gateway: ${apiPath} -> ${Array.isArray(target.host) ? target.host.join(', ') : target.host}:${target.port}`,
881
- priority: options.priority || 100 // Higher priority for specific path matching
882
- };
883
- // Add CORS headers if requested
884
- if (options.addCorsHeaders) {
885
- apiRoute.headers = {
886
- response: {
887
- 'Access-Control-Allow-Origin': '*',
888
- 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
889
- 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
890
- 'Access-Control-Max-Age': '86400'
891
- }
892
- };
893
- }
894
- return mergeRouteConfigs(baseRoute, apiRoute);
895
- }
896
- /**
897
- * Create a rate limiting route pattern
898
- * @param baseRoute Base route to add rate limiting to
899
- * @param rateLimit Rate limiting configuration
900
- * @returns Route with rate limiting
901
- */
902
- export function addRateLimiting(baseRoute, rateLimit) {
903
- return mergeRouteConfigs(baseRoute, {
904
- security: {
905
- rateLimit: {
906
- enabled: true,
907
- maxRequests: rateLimit.maxRequests,
908
- window: rateLimit.window,
909
- keyBy: rateLimit.keyBy || 'ip',
910
- headerName: rateLimit.headerName,
911
- errorMessage: rateLimit.errorMessage || 'Rate limit exceeded. Please try again later.'
912
- }
913
- }
914
- });
915
- }
916
- /**
917
- * Create a basic authentication route pattern
918
- * @param baseRoute Base route to add authentication to
919
- * @param auth Authentication configuration
920
- * @returns Route with basic authentication
921
- */
922
- export function addBasicAuth(baseRoute, auth) {
923
- return mergeRouteConfigs(baseRoute, {
924
- security: {
925
- basicAuth: {
926
- enabled: true,
927
- users: auth.users,
928
- realm: auth.realm || 'Restricted Area',
929
- excludePaths: auth.excludePaths || []
930
- }
931
- }
932
- });
933
- }
934
- /**
935
- * Create a JWT authentication route pattern
936
- * @param baseRoute Base route to add JWT authentication to
937
- * @param jwt JWT authentication configuration
938
- * @returns Route with JWT authentication
939
- */
940
- export function addJwtAuth(baseRoute, jwt) {
941
- return mergeRouteConfigs(baseRoute, {
942
- security: {
943
- jwtAuth: {
944
- enabled: true,
945
- secret: jwt.secret,
946
- algorithm: jwt.algorithm || 'HS256',
947
- issuer: jwt.issuer,
948
- audience: jwt.audience,
949
- expiresIn: jwt.expiresIn,
950
- excludePaths: jwt.excludePaths || []
951
- }
952
- }
953
- });
954
- }
955
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtaGVscGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvdXRpbHMvcm91dGUtaGVscGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0JHO0FBRUgsT0FBTyxLQUFLLE9BQU8sTUFBTSxxQkFBcUIsQ0FBQztBQUUvQyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNyRCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsWUFBWSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDN0UsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sdUNBQXVDLENBQUM7QUFFNUU7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLGVBQWUsQ0FDN0IsT0FBMEIsRUFDMUIsTUFBaUQsRUFDakQsVUFBaUMsRUFBRTtJQUVuQyxxQkFBcUI7SUFDckIsTUFBTSxLQUFLLEdBQWdCO1FBQ3pCLEtBQUssRUFBRSxPQUFPLENBQUMsS0FBSyxFQUFFLEtBQUssSUFBSSxFQUFFO1FBQ2pDLE9BQU87S0FDUixDQUFDO0lBRUYsc0JBQXNCO0lBQ3RCLE1BQU0sTUFBTSxHQUFpQjtRQUMzQixJQUFJLEVBQUUsU0FBUztRQUNmLE9BQU8sRUFBRSxDQUFDLE1BQU0sQ0FBQztLQUNsQixDQUFDO0lBRUYsMEJBQTBCO0lBQzFCLE9BQU87UUFDTCxLQUFLO1FBQ0wsTUFBTTtRQUNOLElBQUksRUFBRSxPQUFPLENBQUMsSUFBSSxJQUFJLGtCQUFrQixLQUFLLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUU7UUFDL0YsR0FBRyxPQUFPO0tBQ1gsQ0FBQztBQUNKLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLFVBQVUseUJBQXlCLENB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
9
+ // Re-export everything from the modular helpers
10
+ export * from './route-helpers/index.js';
11
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtaGVscGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvdXRpbHMvcm91dGUtaGVscGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7OztHQU9HO0FBRUgsZ0RBQWdEO0FBQ2hELGNBQWMsMEJBQTBCLENBQUMifQ==