@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,1308 +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
9
 
21
- import * as plugins from '../../../plugins.js';
22
- import type { IRouteConfig, IRouteMatch, IRouteAction, IRouteTarget, TPortRange, IRouteContext } from '../models/route-types.js';
23
- import { mergeRouteConfigs } from './route-utils.js';
24
- import { ProtocolDetector, HttpDetector } from '../../../detection/index.js';
25
- import { createSocketTracker } from '../../../core/utils/socket-tracker.js';
26
-
27
- /**
28
- * Create an HTTP-only route configuration
29
- * @param domains Domain(s) to match
30
- * @param target Target host and port
31
- * @param options Additional route options
32
- * @returns Route configuration object
33
- */
34
- export function createHttpRoute(
35
- domains: string | string[],
36
- target: { host: string | string[]; port: number },
37
- options: Partial<IRouteConfig> = {}
38
- ): IRouteConfig {
39
- // Create route match
40
- const match: IRouteMatch = {
41
- ports: options.match?.ports || 80,
42
- domains
43
- };
44
-
45
- // Create route action
46
- const action: IRouteAction = {
47
- type: 'forward',
48
- targets: [target]
49
- };
50
-
51
- // Create the route config
52
- return {
53
- match,
54
- action,
55
- name: options.name || `HTTP Route for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
56
- ...options
57
- };
58
- }
59
-
60
- /**
61
- * Create an HTTPS route with TLS termination (including HTTP redirect to HTTPS)
62
- * @param domains Domain(s) to match
63
- * @param target Target host and port
64
- * @param options Additional route options
65
- * @returns Route configuration object
66
- */
67
- export function createHttpsTerminateRoute(
68
- domains: string | string[],
69
- target: { host: string | string[]; port: number },
70
- options: {
71
- certificate?: 'auto' | { key: string; cert: string };
72
- httpPort?: number | number[];
73
- httpsPort?: number | number[];
74
- reencrypt?: boolean;
75
- name?: string;
76
- [key: string]: any;
77
- } = {}
78
- ): IRouteConfig {
79
- // Create route match
80
- const match: IRouteMatch = {
81
- ports: options.httpsPort || 443,
82
- domains
83
- };
84
-
85
- // Create route action
86
- const action: IRouteAction = {
87
- type: 'forward',
88
- targets: [target],
89
- tls: {
90
- mode: options.reencrypt ? 'terminate-and-reencrypt' : 'terminate',
91
- certificate: options.certificate || 'auto'
92
- }
93
- };
94
-
95
- // Create the route config
96
- return {
97
- match,
98
- action,
99
- name: options.name || `HTTPS Route for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
100
- ...options
101
- };
102
- }
103
-
104
- /**
105
- * Create an HTTP to HTTPS redirect route
106
- * @param domains Domain(s) to match
107
- * @param httpsPort HTTPS port to redirect to (default: 443)
108
- * @param options Additional route options
109
- * @returns Route configuration object
110
- */
111
- export function createHttpToHttpsRedirect(
112
- domains: string | string[],
113
- httpsPort: number = 443,
114
- options: Partial<IRouteConfig> = {}
115
- ): IRouteConfig {
116
- // Create route match
117
- const match: IRouteMatch = {
118
- ports: options.match?.ports || 80,
119
- domains
120
- };
121
-
122
- // Create route action
123
- const action: IRouteAction = {
124
- type: 'socket-handler',
125
- socketHandler: SocketHandlers.httpRedirect(`https://{domain}:${httpsPort}{path}`, 301)
126
- };
127
-
128
- // Create the route config
129
- return {
130
- match,
131
- action,
132
- name: options.name || `HTTP to HTTPS Redirect for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
133
- ...options
134
- };
135
- }
136
-
137
- /**
138
- * Create an HTTPS passthrough route (SNI-based forwarding without TLS termination)
139
- * @param domains Domain(s) to match
140
- * @param target Target host and port
141
- * @param options Additional route options
142
- * @returns Route configuration object
143
- */
144
- export function createHttpsPassthroughRoute(
145
- domains: string | string[],
146
- target: { host: string | string[]; port: number },
147
- options: Partial<IRouteConfig> = {}
148
- ): IRouteConfig {
149
- // Create route match
150
- const match: IRouteMatch = {
151
- ports: options.match?.ports || 443,
152
- domains
153
- };
154
-
155
- // Create route action
156
- const action: IRouteAction = {
157
- type: 'forward',
158
- targets: [target],
159
- tls: {
160
- mode: 'passthrough'
161
- }
162
- };
163
-
164
- // Create the route config
165
- return {
166
- match,
167
- action,
168
- name: options.name || `HTTPS Passthrough for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
169
- ...options
170
- };
171
- }
172
-
173
- /**
174
- * Create a complete HTTPS server with HTTP to HTTPS redirects
175
- * @param domains Domain(s) to match
176
- * @param target Target host and port
177
- * @param options Additional configuration options
178
- * @returns Array of two route configurations (HTTPS and HTTP redirect)
179
- */
180
- export function createCompleteHttpsServer(
181
- domains: string | string[],
182
- target: { host: string | string[]; port: number },
183
- options: {
184
- certificate?: 'auto' | { key: string; cert: string };
185
- httpPort?: number | number[];
186
- httpsPort?: number | number[];
187
- reencrypt?: boolean;
188
- name?: string;
189
- [key: string]: any;
190
- } = {}
191
- ): IRouteConfig[] {
192
- // Create the HTTPS route
193
- const httpsRoute = createHttpsTerminateRoute(domains, target, options);
194
-
195
- // Create the HTTP redirect route
196
- const httpRedirectRoute = createHttpToHttpsRedirect(
197
- domains,
198
- // Extract the HTTPS port from the HTTPS route - ensure it's a number
199
- typeof options.httpsPort === 'number' ? options.httpsPort :
200
- Array.isArray(options.httpsPort) ? options.httpsPort[0] : 443,
201
- {
202
- // Set the HTTP port
203
- match: {
204
- ports: options.httpPort || 80,
205
- domains
206
- },
207
- name: `HTTP to HTTPS Redirect for ${Array.isArray(domains) ? domains.join(', ') : domains}`
208
- }
209
- );
210
-
211
- return [httpsRoute, httpRedirectRoute];
212
- }
213
-
214
- /**
215
- * Create a load balancer route (round-robin between multiple backend hosts)
216
- * @param domains Domain(s) to match
217
- * @param backendsOrHosts Array of backend servers OR array of host strings (legacy)
218
- * @param portOrOptions Port number (legacy) OR options object
219
- * @param options Additional route options (legacy)
220
- * @returns Route configuration object
221
- */
222
- export function createLoadBalancerRoute(
223
- domains: string | string[],
224
- backendsOrHosts: Array<{ host: string; port: number }> | string[],
225
- portOrOptions?: number | {
226
- tls?: {
227
- mode: 'passthrough' | 'terminate' | 'terminate-and-reencrypt';
228
- certificate?: 'auto' | { key: string; cert: string };
229
- };
230
- useTls?: boolean;
231
- certificate?: 'auto' | { key: string; cert: string };
232
- algorithm?: 'round-robin' | 'least-connections' | 'ip-hash';
233
- healthCheck?: {
234
- path: string;
235
- interval: number;
236
- timeout: number;
237
- unhealthyThreshold: number;
238
- healthyThreshold: number;
239
- };
240
- [key: string]: any;
241
- },
242
- options?: {
243
- tls?: {
244
- mode: 'passthrough' | 'terminate' | 'terminate-and-reencrypt';
245
- certificate?: 'auto' | { key: string; cert: string };
246
- };
247
- [key: string]: any;
248
- }
249
- ): IRouteConfig {
250
- // Handle legacy signature: (domains, hosts[], port, options)
251
- let backends: Array<{ host: string; port: number }>;
252
- let finalOptions: any;
253
-
254
- if (Array.isArray(backendsOrHosts) && backendsOrHosts.length > 0 && typeof backendsOrHosts[0] === 'string') {
255
- // Legacy signature
256
- const hosts = backendsOrHosts as string[];
257
- const port = portOrOptions as number;
258
- backends = hosts.map(host => ({ host, port }));
259
- finalOptions = options || {};
260
- } else {
261
- // New signature
262
- backends = backendsOrHosts as Array<{ host: string; port: number }>;
263
- finalOptions = (portOrOptions as any) || {};
264
- }
265
-
266
- // Extract hosts and ensure all backends use the same port
267
- const port = backends[0].port;
268
- const hosts = backends.map(backend => backend.host);
269
-
270
- // Create route match
271
- const match: IRouteMatch = {
272
- ports: finalOptions.match?.ports || (finalOptions.tls || finalOptions.useTls ? 443 : 80),
273
- domains
274
- };
275
-
276
- // Create route target
277
- const target: IRouteTarget = {
278
- host: hosts,
279
- port
280
- };
281
-
282
- // Create route action
283
- const action: IRouteAction = {
284
- type: 'forward',
285
- targets: [target]
286
- };
287
-
288
- // Add TLS configuration if provided
289
- if (finalOptions.tls || finalOptions.useTls) {
290
- action.tls = {
291
- mode: finalOptions.tls?.mode || 'terminate',
292
- certificate: finalOptions.tls?.certificate || finalOptions.certificate || 'auto'
293
- };
294
- }
295
-
296
- // Add load balancing options
297
- if (finalOptions.algorithm || finalOptions.healthCheck) {
298
- action.loadBalancing = {
299
- algorithm: finalOptions.algorithm || 'round-robin',
300
- healthCheck: finalOptions.healthCheck
301
- };
302
- }
303
-
304
- // Create the route config
305
- return {
306
- match,
307
- action,
308
- name: finalOptions.name || `Load Balancer for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
309
- ...finalOptions
310
- };
311
- }
312
-
313
- /**
314
- * Create an API route configuration
315
- * @param domains Domain(s) to match
316
- * @param apiPath API base path (e.g., "/api")
317
- * @param target Target host and port
318
- * @param options Additional route options
319
- * @returns Route configuration object
320
- */
321
- export function createApiRoute(
322
- domains: string | string[],
323
- apiPath: string,
324
- target: { host: string | string[]; port: number },
325
- options: {
326
- useTls?: boolean;
327
- certificate?: 'auto' | { key: string; cert: string };
328
- addCorsHeaders?: boolean;
329
- httpPort?: number | number[];
330
- httpsPort?: number | number[];
331
- name?: string;
332
- [key: string]: any;
333
- } = {}
334
- ): IRouteConfig {
335
- // Normalize API path
336
- const normalizedPath = apiPath.startsWith('/') ? apiPath : `/${apiPath}`;
337
- const pathWithWildcard = normalizedPath.endsWith('/')
338
- ? `${normalizedPath}*`
339
- : `${normalizedPath}/*`;
340
-
341
- // Create route match
342
- const match: IRouteMatch = {
343
- ports: options.useTls
344
- ? (options.httpsPort || 443)
345
- : (options.httpPort || 80),
346
- domains,
347
- path: pathWithWildcard
348
- };
349
-
350
- // Create route action
351
- const action: IRouteAction = {
352
- type: 'forward',
353
- targets: [target]
354
- };
355
-
356
- // Add TLS configuration if using HTTPS
357
- if (options.useTls) {
358
- action.tls = {
359
- mode: 'terminate',
360
- certificate: options.certificate || 'auto'
361
- };
362
- }
363
-
364
- // Add CORS headers if requested
365
- const headers: Record<string, Record<string, string>> = {};
366
- if (options.addCorsHeaders) {
367
- headers.response = {
368
- 'Access-Control-Allow-Origin': '*',
369
- 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
370
- 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
371
- 'Access-Control-Max-Age': '86400'
372
- };
373
- }
374
-
375
- // Create the route config
376
- return {
377
- match,
378
- action,
379
- headers: Object.keys(headers).length > 0 ? headers : undefined,
380
- name: options.name || `API Route ${normalizedPath} for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
381
- priority: options.priority || 100, // Higher priority for specific path matches
382
- ...options
383
- };
384
- }
385
-
386
- /**
387
- * Create a WebSocket route configuration
388
- * @param domains Domain(s) to match
389
- * @param targetOrPath Target server OR WebSocket path (legacy)
390
- * @param targetOrOptions Target server (legacy) OR options
391
- * @param options Additional route options (legacy)
392
- * @returns Route configuration object
393
- */
394
- export function createWebSocketRoute(
395
- domains: string | string[],
396
- targetOrPath: { host: string | string[]; port: number } | string,
397
- targetOrOptions?: { host: string | string[]; port: number } | {
398
- useTls?: boolean;
399
- certificate?: 'auto' | { key: string; cert: string };
400
- path?: string;
401
- httpPort?: number | number[];
402
- httpsPort?: number | number[];
403
- pingInterval?: number;
404
- pingTimeout?: number;
405
- name?: string;
406
- [key: string]: any;
407
- },
408
- options?: {
409
- useTls?: boolean;
410
- certificate?: 'auto' | { key: string; cert: string };
411
- httpPort?: number | number[];
412
- httpsPort?: number | number[];
413
- pingInterval?: number;
414
- pingTimeout?: number;
415
- name?: string;
416
- [key: string]: any;
417
- }
418
- ): IRouteConfig {
419
- // Handle different signatures
420
- let target: { host: string | string[]; port: number };
421
- let wsPath: string;
422
- let finalOptions: any;
423
-
424
- if (typeof targetOrPath === 'string') {
425
- // Legacy signature: (domains, path, target, options)
426
- wsPath = targetOrPath;
427
- target = targetOrOptions as { host: string | string[]; port: number };
428
- finalOptions = options || {};
429
- } else {
430
- // New signature: (domains, target, options)
431
- target = targetOrPath;
432
- finalOptions = (targetOrOptions as any) || {};
433
- wsPath = finalOptions.path || '/ws';
434
- }
435
-
436
- // Normalize WebSocket path
437
- const normalizedPath = wsPath.startsWith('/') ? wsPath : `/${wsPath}`;
438
-
439
- // Create route match
440
- const match: IRouteMatch = {
441
- ports: finalOptions.useTls
442
- ? (finalOptions.httpsPort || 443)
443
- : (finalOptions.httpPort || 80),
444
- domains,
445
- path: normalizedPath
446
- };
447
-
448
- // Create route action
449
- const action: IRouteAction = {
450
- type: 'forward',
451
- targets: [target],
452
- websocket: {
453
- enabled: true,
454
- pingInterval: finalOptions.pingInterval || 30000, // 30 seconds
455
- pingTimeout: finalOptions.pingTimeout || 5000 // 5 seconds
456
- }
457
- };
458
-
459
- // Add TLS configuration if using HTTPS
460
- if (finalOptions.useTls) {
461
- action.tls = {
462
- mode: 'terminate',
463
- certificate: finalOptions.certificate || 'auto'
464
- };
465
- }
466
-
467
- // Create the route config
468
- return {
469
- match,
470
- action,
471
- name: finalOptions.name || `WebSocket Route ${normalizedPath} for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
472
- priority: finalOptions.priority || 100, // Higher priority for WebSocket routes
473
- ...finalOptions
474
- };
475
- }
476
-
477
- /**
478
- * Create a helper function that applies a port offset
479
- * @param offset The offset to apply to the matched port
480
- * @returns A function that adds the offset to the matched port
481
- */
482
- export function createPortOffset(offset: number): (context: IRouteContext) => number {
483
- return (context: IRouteContext) => context.port + offset;
484
- }
485
-
486
- /**
487
- * Create a port mapping route with context-based port function
488
- * @param options Port mapping route options
489
- * @returns Route configuration object
490
- */
491
- export function createPortMappingRoute(options: {
492
- sourcePortRange: TPortRange;
493
- targetHost: string | string[] | ((context: IRouteContext) => string | string[]);
494
- portMapper: (context: IRouteContext) => number;
495
- name?: string;
496
- domains?: string | string[];
497
- priority?: number;
498
- [key: string]: any;
499
- }): IRouteConfig {
500
- // Create route match
501
- const match: IRouteMatch = {
502
- ports: options.sourcePortRange,
503
- domains: options.domains
504
- };
505
-
506
- // Create route action
507
- const action: IRouteAction = {
508
- type: 'forward',
509
- targets: [{
510
- host: options.targetHost,
511
- port: options.portMapper
512
- }]
513
- };
514
-
515
- // Create the route config
516
- return {
517
- match,
518
- action,
519
- name: options.name || `Port Mapping Route for ${options.domains || 'all domains'}`,
520
- priority: options.priority,
521
- ...options
522
- };
523
- }
524
-
525
- /**
526
- * Create a simple offset port mapping route
527
- * @param options Offset port mapping route options
528
- * @returns Route configuration object
529
- */
530
- export function createOffsetPortMappingRoute(options: {
531
- ports: TPortRange;
532
- targetHost: string | string[];
533
- offset: number;
534
- name?: string;
535
- domains?: string | string[];
536
- priority?: number;
537
- [key: string]: any;
538
- }): IRouteConfig {
539
- return createPortMappingRoute({
540
- sourcePortRange: options.ports,
541
- targetHost: options.targetHost,
542
- portMapper: (context) => context.port + options.offset,
543
- name: options.name || `Offset Mapping (${options.offset > 0 ? '+' : ''}${options.offset}) for ${options.domains || 'all domains'}`,
544
- domains: options.domains,
545
- priority: options.priority,
546
- ...options
547
- });
548
- }
549
-
550
- /**
551
- * Create a dynamic route with context-based host and port mapping
552
- * @param options Dynamic route options
553
- * @returns Route configuration object
554
- */
555
- export function createDynamicRoute(options: {
556
- ports: TPortRange;
557
- targetHost: (context: IRouteContext) => string | string[];
558
- portMapper: (context: IRouteContext) => number;
559
- name?: string;
560
- domains?: string | string[];
561
- path?: string;
562
- clientIp?: string[];
563
- priority?: number;
564
- [key: string]: any;
565
- }): IRouteConfig {
566
- // Create route match
567
- const match: IRouteMatch = {
568
- ports: options.ports,
569
- domains: options.domains,
570
- path: options.path,
571
- clientIp: options.clientIp
572
- };
573
-
574
- // Create route action
575
- const action: IRouteAction = {
576
- type: 'forward',
577
- targets: [{
578
- host: options.targetHost,
579
- port: options.portMapper
580
- }]
581
- };
582
-
583
- // Create the route config
584
- return {
585
- match,
586
- action,
587
- name: options.name || `Dynamic Route for ${options.domains || 'all domains'}`,
588
- priority: options.priority,
589
- ...options
590
- };
591
- }
592
-
593
- /**
594
- * Create a smart load balancer with dynamic domain-based backend selection
595
- * @param options Smart load balancer options
596
- * @returns Route configuration object
597
- */
598
- export function createSmartLoadBalancer(options: {
599
- ports: TPortRange;
600
- domainTargets: Record<string, string | string[]>;
601
- portMapper: (context: IRouteContext) => number;
602
- name?: string;
603
- defaultTarget?: string | string[];
604
- priority?: number;
605
- [key: string]: any;
606
- }): IRouteConfig {
607
- // Extract all domain keys to create the match criteria
608
- const domains = Object.keys(options.domainTargets);
609
-
610
- // Create the smart host selector function
611
- const hostSelector = (context: IRouteContext) => {
612
- const domain = context.domain || '';
613
- return options.domainTargets[domain] || options.defaultTarget || 'localhost';
614
- };
615
-
616
- // Create route match
617
- const match: IRouteMatch = {
618
- ports: options.ports,
619
- domains
620
- };
621
-
622
- // Create route action
623
- const action: IRouteAction = {
624
- type: 'forward',
625
- targets: [{
626
- host: hostSelector,
627
- port: options.portMapper
628
- }]
629
- };
630
-
631
- // Create the route config
632
- return {
633
- match,
634
- action,
635
- name: options.name || `Smart Load Balancer for ${domains.join(', ')}`,
636
- priority: options.priority,
637
- ...options
638
- };
639
- }
640
-
641
- /**
642
- * Create an NFTables-based route for high-performance packet forwarding
643
- * @param nameOrDomains Name or domain(s) to match
644
- * @param target Target host and port
645
- * @param options Additional route options
646
- * @returns Route configuration object
647
- */
648
- export function createNfTablesRoute(
649
- nameOrDomains: string | string[],
650
- target: { host: string; port: number | 'preserve' },
651
- options: {
652
- ports?: TPortRange;
653
- protocol?: 'tcp' | 'udp' | 'all';
654
- preserveSourceIP?: boolean;
655
- ipAllowList?: string[];
656
- ipBlockList?: string[];
657
- maxRate?: string;
658
- priority?: number;
659
- useTls?: boolean;
660
- tableName?: string;
661
- useIPSets?: boolean;
662
- useAdvancedNAT?: boolean;
663
- } = {}
664
- ): IRouteConfig {
665
- // Determine if this is a name or domain
666
- let name: string;
667
- let domains: string | string[] | undefined;
668
-
669
- if (Array.isArray(nameOrDomains) || (typeof nameOrDomains === 'string' && nameOrDomains.includes('.'))) {
670
- domains = nameOrDomains;
671
- name = Array.isArray(nameOrDomains) ? nameOrDomains[0] : nameOrDomains;
672
- } else {
673
- name = nameOrDomains;
674
- domains = undefined; // No domains
675
- }
676
-
677
- // Create route match
678
- const match: IRouteMatch = {
679
- domains,
680
- ports: options.ports || 80
681
- };
682
-
683
- // Create route action
684
- const action: IRouteAction = {
685
- type: 'forward',
686
- targets: [{
687
- host: target.host,
688
- port: target.port
689
- }],
690
- forwardingEngine: 'nftables',
691
- nftables: {
692
- protocol: options.protocol || 'tcp',
693
- preserveSourceIP: options.preserveSourceIP,
694
- maxRate: options.maxRate,
695
- priority: options.priority,
696
- tableName: options.tableName,
697
- useIPSets: options.useIPSets,
698
- useAdvancedNAT: options.useAdvancedNAT
699
- }
700
- };
701
-
702
- // Add TLS options if needed
703
- if (options.useTls) {
704
- action.tls = {
705
- mode: 'passthrough'
706
- };
707
- }
708
-
709
- // Create the route config
710
- const routeConfig: IRouteConfig = {
711
- name,
712
- match,
713
- action
714
- };
715
-
716
- // Add security if allowed or blocked IPs are specified
717
- if (options.ipAllowList?.length || options.ipBlockList?.length) {
718
- routeConfig.security = {
719
- ipAllowList: options.ipAllowList,
720
- ipBlockList: options.ipBlockList
721
- };
722
- }
723
-
724
- return routeConfig;
725
- }
726
-
727
- /**
728
- * Create an NFTables-based TLS termination route
729
- * @param nameOrDomains Name or domain(s) to match
730
- * @param target Target host and port
731
- * @param options Additional route options
732
- * @returns Route configuration object
733
- */
734
- export function createNfTablesTerminateRoute(
735
- nameOrDomains: string | string[],
736
- target: { host: string; port: number | 'preserve' },
737
- options: {
738
- ports?: TPortRange;
739
- protocol?: 'tcp' | 'udp' | 'all';
740
- preserveSourceIP?: boolean;
741
- ipAllowList?: string[];
742
- ipBlockList?: string[];
743
- maxRate?: string;
744
- priority?: number;
745
- tableName?: string;
746
- useIPSets?: boolean;
747
- useAdvancedNAT?: boolean;
748
- certificate?: 'auto' | { key: string; cert: string };
749
- } = {}
750
- ): IRouteConfig {
751
- // Create basic NFTables route
752
- const route = createNfTablesRoute(
753
- nameOrDomains,
754
- target,
755
- {
756
- ...options,
757
- ports: options.ports || 443,
758
- useTls: false
759
- }
760
- );
761
-
762
- // Set TLS termination
763
- route.action.tls = {
764
- mode: 'terminate',
765
- certificate: options.certificate || 'auto'
766
- };
767
-
768
- return route;
769
- }
770
-
771
- /**
772
- * Create a complete NFTables-based HTTPS setup with HTTP redirect
773
- * @param nameOrDomains Name or domain(s) to match
774
- * @param target Target host and port
775
- * @param options Additional route options
776
- * @returns Array of two route configurations (HTTPS and HTTP redirect)
777
- */
778
- export function createCompleteNfTablesHttpsServer(
779
- nameOrDomains: string | string[],
780
- target: { host: string; port: number | 'preserve' },
781
- options: {
782
- httpPort?: TPortRange;
783
- httpsPort?: TPortRange;
784
- protocol?: 'tcp' | 'udp' | 'all';
785
- preserveSourceIP?: boolean;
786
- ipAllowList?: string[];
787
- ipBlockList?: string[];
788
- maxRate?: string;
789
- priority?: number;
790
- tableName?: string;
791
- useIPSets?: boolean;
792
- useAdvancedNAT?: boolean;
793
- certificate?: 'auto' | { key: string; cert: string };
794
- } = {}
795
- ): IRouteConfig[] {
796
- // Create the HTTPS route using NFTables
797
- const httpsRoute = createNfTablesTerminateRoute(
798
- nameOrDomains,
799
- target,
800
- {
801
- ...options,
802
- ports: options.httpsPort || 443
803
- }
804
- );
805
-
806
- // Determine the domain(s) for HTTP redirect
807
- const domains = typeof nameOrDomains === 'string' && !nameOrDomains.includes('.')
808
- ? undefined
809
- : nameOrDomains;
810
-
811
- // Extract the HTTPS port for the redirect destination
812
- const httpsPort = typeof options.httpsPort === 'number'
813
- ? options.httpsPort
814
- : Array.isArray(options.httpsPort) && typeof options.httpsPort[0] === 'number'
815
- ? options.httpsPort[0]
816
- : 443;
817
-
818
- // Create the HTTP redirect route (this uses standard forwarding, not NFTables)
819
- const httpRedirectRoute = createHttpToHttpsRedirect(
820
- domains as any, // Type cast needed since domains can be undefined now
821
- httpsPort,
822
- {
823
- match: {
824
- ports: options.httpPort || 80,
825
- domains: domains as any // Type cast needed since domains can be undefined now
826
- },
827
- name: `HTTP to HTTPS Redirect for ${Array.isArray(domains) ? domains.join(', ') : domains || 'all domains'}`
828
- }
829
- );
830
-
831
- return [httpsRoute, httpRedirectRoute];
832
- }
833
-
834
- /**
835
- * Create a socket handler route configuration
836
- * @param domains Domain(s) to match
837
- * @param ports Port(s) to listen on
838
- * @param handler Socket handler function
839
- * @param options Additional route options
840
- * @returns Route configuration object
841
- */
842
- export function createSocketHandlerRoute(
843
- domains: string | string[],
844
- ports: TPortRange,
845
- handler: (socket: plugins.net.Socket) => void | Promise<void>,
846
- options: {
847
- name?: string;
848
- priority?: number;
849
- path?: string;
850
- } = {}
851
- ): IRouteConfig {
852
- return {
853
- name: options.name || 'socket-handler-route',
854
- priority: options.priority !== undefined ? options.priority : 50,
855
- match: {
856
- domains,
857
- ports,
858
- ...(options.path && { path: options.path })
859
- },
860
- action: {
861
- type: 'socket-handler',
862
- socketHandler: handler
863
- }
864
- };
865
- }
866
-
867
- /**
868
- * Pre-built socket handlers for common use cases
869
- */
870
- export const SocketHandlers = {
871
- /**
872
- * Simple echo server handler
873
- */
874
- echo: (socket: plugins.net.Socket, context: IRouteContext) => {
875
- socket.write('ECHO SERVER READY\n');
876
- socket.on('data', data => socket.write(data));
877
- },
878
-
879
- /**
880
- * TCP proxy handler
881
- */
882
- proxy: (targetHost: string, targetPort: number) => (socket: plugins.net.Socket, context: IRouteContext) => {
883
- const target = plugins.net.connect(targetPort, targetHost);
884
- socket.pipe(target);
885
- target.pipe(socket);
886
- socket.on('close', () => target.destroy());
887
- target.on('close', () => socket.destroy());
888
- target.on('error', (err) => {
889
- console.error('Proxy target error:', err);
890
- socket.destroy();
891
- });
892
- },
893
-
894
- /**
895
- * Line-based protocol handler
896
- */
897
- lineProtocol: (handler: (line: string, socket: plugins.net.Socket) => void) => (socket: plugins.net.Socket, context: IRouteContext) => {
898
- let buffer = '';
899
- socket.on('data', (data) => {
900
- buffer += data.toString();
901
- const lines = buffer.split('\n');
902
- buffer = lines.pop() || '';
903
- lines.forEach(line => {
904
- if (line.trim()) {
905
- handler(line.trim(), socket);
906
- }
907
- });
908
- });
909
- },
910
-
911
- /**
912
- * Simple HTTP response handler (for testing)
913
- */
914
- httpResponse: (statusCode: number, body: string) => (socket: plugins.net.Socket, context: IRouteContext) => {
915
- const response = [
916
- `HTTP/1.1 ${statusCode} ${statusCode === 200 ? 'OK' : 'Error'}`,
917
- 'Content-Type: text/plain',
918
- `Content-Length: ${body.length}`,
919
- 'Connection: close',
920
- '',
921
- body
922
- ].join('\r\n');
923
-
924
- socket.write(response);
925
- socket.end();
926
- },
927
-
928
- /**
929
- * Block connection immediately
930
- */
931
- block: (message?: string) => (socket: plugins.net.Socket, context: IRouteContext) => {
932
- const finalMessage = message || `Connection blocked from ${context.clientIp}`;
933
- if (finalMessage) {
934
- socket.write(finalMessage);
935
- }
936
- socket.end();
937
- },
938
-
939
- /**
940
- * HTTP block response
941
- */
942
- httpBlock: (statusCode: number = 403, message?: string) => (socket: plugins.net.Socket, context: IRouteContext) => {
943
- const defaultMessage = `Access forbidden for ${context.domain || context.clientIp}`;
944
- const finalMessage = message || defaultMessage;
945
-
946
- const response = [
947
- `HTTP/1.1 ${statusCode} ${finalMessage}`,
948
- 'Content-Type: text/plain',
949
- `Content-Length: ${finalMessage.length}`,
950
- 'Connection: close',
951
- '',
952
- finalMessage
953
- ].join('\r\n');
954
-
955
- socket.write(response);
956
- socket.end();
957
- },
958
-
959
- /**
960
- * HTTP redirect handler
961
- * Now uses the centralized detection module for HTTP parsing
962
- */
963
- httpRedirect: (locationTemplate: string, statusCode: number = 301) => (socket: plugins.net.Socket, context: IRouteContext) => {
964
- const tracker = createSocketTracker(socket);
965
- const connectionId = ProtocolDetector.createConnectionId({
966
- socketId: context.connectionId || `${Date.now()}-${Math.random()}`
967
- });
968
-
969
- const handleData = async (data: Buffer) => {
970
- // Use detection module for parsing
971
- const detectionResult = await ProtocolDetector.detectWithConnectionTracking(
972
- data,
973
- connectionId,
974
- { extractFullHeaders: false } // We only need method and path
975
- );
976
-
977
- if (detectionResult.protocol === 'http' && detectionResult.connectionInfo.path) {
978
- const method = detectionResult.connectionInfo.method || 'GET';
979
- const path = detectionResult.connectionInfo.path || '/';
980
-
981
- const domain = context.domain || 'localhost';
982
- const port = context.port;
983
-
984
- let finalLocation = locationTemplate
985
- .replace('{domain}', domain)
986
- .replace('{port}', String(port))
987
- .replace('{path}', path)
988
- .replace('{clientIp}', context.clientIp);
989
-
990
- const message = `Redirecting to ${finalLocation}`;
991
- const response = [
992
- `HTTP/1.1 ${statusCode} ${statusCode === 301 ? 'Moved Permanently' : 'Found'}`,
993
- `Location: ${finalLocation}`,
994
- 'Content-Type: text/plain',
995
- `Content-Length: ${message.length}`,
996
- 'Connection: close',
997
- '',
998
- message
999
- ].join('\r\n');
1000
-
1001
- socket.write(response);
1002
- } else {
1003
- // Not a valid HTTP request, close connection
1004
- socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
1005
- }
1006
-
1007
- socket.end();
1008
- // Clean up detection state
1009
- ProtocolDetector.cleanupConnections();
1010
- // Clean up all tracked resources
1011
- tracker.cleanup();
1012
- };
1013
-
1014
- // Use tracker to manage the listener
1015
- socket.once('data', handleData);
1016
-
1017
- tracker.addListener('error', (err) => {
1018
- tracker.safeDestroy(err);
1019
- });
1020
-
1021
- tracker.addListener('close', () => {
1022
- tracker.cleanup();
1023
- });
1024
- },
1025
-
1026
- /**
1027
- * HTTP server handler for ACME challenges and other HTTP needs
1028
- * Now uses the centralized detection module for HTTP parsing
1029
- */
1030
- httpServer: (handler: (req: { method: string; url: string; headers: Record<string, string>; body?: string }, res: { status: (code: number) => void; header: (name: string, value: string) => void; send: (data: string) => void; end: () => void }) => void) => (socket: plugins.net.Socket, context: IRouteContext) => {
1031
- const tracker = createSocketTracker(socket);
1032
- let requestParsed = false;
1033
- let responseTimer: NodeJS.Timeout | null = null;
1034
- const connectionId = ProtocolDetector.createConnectionId({
1035
- socketId: context.connectionId || `${Date.now()}-${Math.random()}`
1036
- });
1037
-
1038
- const processData = async (data: Buffer) => {
1039
- if (requestParsed) return; // Only handle the first request
1040
-
1041
- // Use HttpDetector for parsing
1042
- const detectionResult = await ProtocolDetector.detectWithConnectionTracking(
1043
- data,
1044
- connectionId,
1045
- { extractFullHeaders: true }
1046
- );
1047
-
1048
- if (detectionResult.protocol !== 'http' || !detectionResult.isComplete) {
1049
- // Not a complete HTTP request yet
1050
- return;
1051
- }
1052
-
1053
- requestParsed = true;
1054
- // Remove data listener after parsing request
1055
- socket.removeListener('data', processData);
1056
- const connInfo = detectionResult.connectionInfo;
1057
-
1058
- // Create request object from detection result
1059
- const req = {
1060
- method: connInfo.method || 'GET',
1061
- url: connInfo.path || '/',
1062
- headers: connInfo.headers || {},
1063
- body: detectionResult.remainingBuffer?.toString() || ''
1064
- };
1065
-
1066
- // Create response object
1067
- let statusCode = 200;
1068
- const responseHeaders: Record<string, string> = {};
1069
- let ended = false;
1070
-
1071
- const res = {
1072
- status: (code: number) => {
1073
- statusCode = code;
1074
- },
1075
- header: (name: string, value: string) => {
1076
- responseHeaders[name] = value;
1077
- },
1078
- send: (data: string) => {
1079
- if (ended) return;
1080
- ended = true;
1081
-
1082
- // Clear response timer since we're sending now
1083
- if (responseTimer) {
1084
- clearTimeout(responseTimer);
1085
- responseTimer = null;
1086
- }
1087
-
1088
- if (!responseHeaders['content-type']) {
1089
- responseHeaders['content-type'] = 'text/plain';
1090
- }
1091
- responseHeaders['content-length'] = String(data.length);
1092
- responseHeaders['connection'] = 'close';
1093
-
1094
- const statusText = statusCode === 200 ? 'OK' :
1095
- statusCode === 404 ? 'Not Found' :
1096
- statusCode === 500 ? 'Internal Server Error' : 'Response';
1097
-
1098
- let response = `HTTP/1.1 ${statusCode} ${statusText}\r\n`;
1099
- for (const [name, value] of Object.entries(responseHeaders)) {
1100
- response += `${name}: ${value}\r\n`;
1101
- }
1102
- response += '\r\n';
1103
- response += data;
1104
-
1105
- socket.write(response);
1106
- socket.end();
1107
- },
1108
- end: () => {
1109
- if (ended) return;
1110
- ended = true;
1111
- socket.write('HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n');
1112
- socket.end();
1113
- }
1114
- };
1115
-
1116
- try {
1117
- handler(req, res);
1118
- // Ensure response is sent even if handler doesn't call send()
1119
- responseTimer = setTimeout(() => {
1120
- if (!ended) {
1121
- res.send('');
1122
- }
1123
- responseTimer = null;
1124
- }, 1000);
1125
- // Track and unref the timer
1126
- tracker.addTimer(responseTimer);
1127
- } catch (error) {
1128
- if (!ended) {
1129
- res.status(500);
1130
- res.send('Internal Server Error');
1131
- }
1132
- // Use safeDestroy for error cases
1133
- tracker.safeDestroy(error instanceof Error ? error : new Error('Handler error'));
1134
- }
1135
- };
1136
-
1137
- // Use tracker to manage listeners
1138
- tracker.addListener('data', processData);
1139
-
1140
- tracker.addListener('error', (err) => {
1141
- if (!requestParsed) {
1142
- tracker.safeDestroy(err);
1143
- }
1144
- });
1145
-
1146
- tracker.addListener('close', () => {
1147
- // Cleanup is handled by tracker
1148
- // Clear any pending response timer
1149
- if (responseTimer) {
1150
- clearTimeout(responseTimer);
1151
- responseTimer = null;
1152
- }
1153
- // Clean up detection state
1154
- ProtocolDetector.cleanupConnections();
1155
- // Clean up all tracked resources
1156
- tracker.cleanup();
1157
- });
1158
- }
1159
- };
1160
-
1161
- /**
1162
- * Create an API Gateway route pattern
1163
- * @param domains Domain(s) to match
1164
- * @param apiBasePath Base path for API endpoints (e.g., '/api')
1165
- * @param target Target host and port
1166
- * @param options Additional route options
1167
- * @returns API route configuration
1168
- */
1169
- export function createApiGatewayRoute(
1170
- domains: string | string[],
1171
- apiBasePath: string,
1172
- target: { host: string | string[]; port: number },
1173
- options: {
1174
- useTls?: boolean;
1175
- certificate?: 'auto' | { key: string; cert: string };
1176
- addCorsHeaders?: boolean;
1177
- [key: string]: any;
1178
- } = {}
1179
- ): IRouteConfig {
1180
- // Normalize apiBasePath to ensure it starts with / and doesn't end with /
1181
- const normalizedPath = apiBasePath.startsWith('/')
1182
- ? apiBasePath
1183
- : `/${apiBasePath}`;
1184
-
1185
- // Add wildcard to path to match all API endpoints
1186
- const apiPath = normalizedPath.endsWith('/')
1187
- ? `${normalizedPath}*`
1188
- : `${normalizedPath}/*`;
1189
-
1190
- // Create base route
1191
- const baseRoute = options.useTls
1192
- ? createHttpsTerminateRoute(domains, target, {
1193
- certificate: options.certificate || 'auto'
1194
- })
1195
- : createHttpRoute(domains, target);
1196
-
1197
- // Add API-specific configurations
1198
- const apiRoute: Partial<IRouteConfig> = {
1199
- match: {
1200
- ...baseRoute.match,
1201
- path: apiPath
1202
- },
1203
- name: options.name || `API Gateway: ${apiPath} -> ${Array.isArray(target.host) ? target.host.join(', ') : target.host}:${target.port}`,
1204
- priority: options.priority || 100 // Higher priority for specific path matching
1205
- };
1206
-
1207
- // Add CORS headers if requested
1208
- if (options.addCorsHeaders) {
1209
- apiRoute.headers = {
1210
- response: {
1211
- 'Access-Control-Allow-Origin': '*',
1212
- 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
1213
- 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
1214
- 'Access-Control-Max-Age': '86400'
1215
- }
1216
- };
1217
- }
1218
-
1219
- return mergeRouteConfigs(baseRoute, apiRoute);
1220
- }
1221
-
1222
- /**
1223
- * Create a rate limiting route pattern
1224
- * @param baseRoute Base route to add rate limiting to
1225
- * @param rateLimit Rate limiting configuration
1226
- * @returns Route with rate limiting
1227
- */
1228
- export function addRateLimiting(
1229
- baseRoute: IRouteConfig,
1230
- rateLimit: {
1231
- maxRequests: number;
1232
- window: number; // Time window in seconds
1233
- keyBy?: 'ip' | 'path' | 'header';
1234
- headerName?: string; // Required if keyBy is 'header'
1235
- errorMessage?: string;
1236
- }
1237
- ): IRouteConfig {
1238
- return mergeRouteConfigs(baseRoute, {
1239
- security: {
1240
- rateLimit: {
1241
- enabled: true,
1242
- maxRequests: rateLimit.maxRequests,
1243
- window: rateLimit.window,
1244
- keyBy: rateLimit.keyBy || 'ip',
1245
- headerName: rateLimit.headerName,
1246
- errorMessage: rateLimit.errorMessage || 'Rate limit exceeded. Please try again later.'
1247
- }
1248
- }
1249
- });
1250
- }
1251
-
1252
- /**
1253
- * Create a basic authentication route pattern
1254
- * @param baseRoute Base route to add authentication to
1255
- * @param auth Authentication configuration
1256
- * @returns Route with basic authentication
1257
- */
1258
- export function addBasicAuth(
1259
- baseRoute: IRouteConfig,
1260
- auth: {
1261
- users: Array<{ username: string; password: string }>;
1262
- realm?: string;
1263
- excludePaths?: string[];
1264
- }
1265
- ): IRouteConfig {
1266
- return mergeRouteConfigs(baseRoute, {
1267
- security: {
1268
- basicAuth: {
1269
- enabled: true,
1270
- users: auth.users,
1271
- realm: auth.realm || 'Restricted Area',
1272
- excludePaths: auth.excludePaths || []
1273
- }
1274
- }
1275
- });
1276
- }
1277
-
1278
- /**
1279
- * Create a JWT authentication route pattern
1280
- * @param baseRoute Base route to add JWT authentication to
1281
- * @param jwt JWT authentication configuration
1282
- * @returns Route with JWT authentication
1283
- */
1284
- export function addJwtAuth(
1285
- baseRoute: IRouteConfig,
1286
- jwt: {
1287
- secret: string;
1288
- algorithm?: string;
1289
- issuer?: string;
1290
- audience?: string;
1291
- expiresIn?: number; // Time in seconds
1292
- excludePaths?: string[];
1293
- }
1294
- ): IRouteConfig {
1295
- return mergeRouteConfigs(baseRoute, {
1296
- security: {
1297
- jwtAuth: {
1298
- enabled: true,
1299
- secret: jwt.secret,
1300
- algorithm: jwt.algorithm || 'HS256',
1301
- issuer: jwt.issuer,
1302
- audience: jwt.audience,
1303
- expiresIn: jwt.expiresIn,
1304
- excludePaths: jwt.excludePaths || []
1305
- }
1306
- }
1307
- });
1308
- }
10
+ // Re-export everything from the modular helpers
11
+ export * from './route-helpers/index.js';