@push.rocks/smartproxy 21.1.7 → 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.
- package/changelog.md +81 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/core/utils/shared-security-manager.d.ts +17 -0
- package/dist_ts/core/utils/shared-security-manager.js +66 -1
- package/dist_ts/proxies/http-proxy/default-certificates.d.ts +54 -0
- package/dist_ts/proxies/http-proxy/default-certificates.js +127 -0
- package/dist_ts/proxies/http-proxy/http-proxy.d.ts +1 -1
- package/dist_ts/proxies/http-proxy/http-proxy.js +9 -14
- package/dist_ts/proxies/http-proxy/index.d.ts +5 -1
- package/dist_ts/proxies/http-proxy/index.js +6 -2
- package/dist_ts/proxies/http-proxy/security-manager.d.ts +4 -12
- package/dist_ts/proxies/http-proxy/security-manager.js +66 -99
- package/dist_ts/proxies/nftables-proxy/index.d.ts +1 -0
- package/dist_ts/proxies/nftables-proxy/index.js +2 -1
- package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +4 -26
- package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +84 -236
- package/dist_ts/proxies/nftables-proxy/utils/index.d.ts +9 -0
- package/dist_ts/proxies/nftables-proxy/utils/index.js +12 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.d.ts +66 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.js +131 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.d.ts +39 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.js +112 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.d.ts +59 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.js +130 -0
- package/dist_ts/proxies/smart-proxy/certificate-manager.js +4 -3
- package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +13 -2
- package/dist_ts/proxies/smart-proxy/connection-manager.js +16 -6
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +35 -10
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +0 -1
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +17 -0
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +72 -9
- package/dist_ts/proxies/smart-proxy/security-manager.d.ts +14 -12
- package/dist_ts/proxies/smart-proxy/security-manager.js +80 -74
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +1 -2
- package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +2 -9
- package/dist_ts/proxies/smart-proxy/tls-manager.js +3 -26
- package/dist_ts/proxies/smart-proxy/utils/index.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/utils/index.js +3 -4
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.d.ts +49 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.js +108 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.d.ts +57 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.js +89 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.d.ts +17 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.js +32 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.d.ts +68 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.js +117 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.d.ts +17 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.js +27 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.d.ts +63 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.js +105 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.d.ts +83 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.js +126 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.d.ts +47 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.js +66 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.d.ts +70 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.js +287 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.d.ts +46 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.js +67 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +4 -457
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +6 -950
- package/dist_ts/proxies/smart-proxy/utils/route-utils.js +2 -2
- package/dist_ts/proxies/smart-proxy/utils/route-validator.d.ts +67 -1
- package/dist_ts/proxies/smart-proxy/utils/route-validator.js +251 -3
- package/npmextra.json +12 -6
- package/package.json +34 -24
- package/readme.hints.md +184 -1
- package/readme.md +235 -172
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/core/utils/shared-security-manager.ts +98 -13
- package/ts/proxies/http-proxy/default-certificates.ts +150 -0
- package/ts/proxies/http-proxy/http-proxy.ts +9 -15
- package/ts/proxies/http-proxy/index.ts +6 -1
- package/ts/proxies/http-proxy/security-manager.ts +141 -161
- package/ts/proxies/nftables-proxy/index.ts +1 -0
- package/ts/proxies/nftables-proxy/nftables-proxy.ts +116 -290
- package/ts/proxies/nftables-proxy/utils/index.ts +38 -0
- package/ts/proxies/nftables-proxy/utils/nft-command-executor.ts +162 -0
- package/ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.ts +125 -0
- package/ts/proxies/nftables-proxy/utils/nft-rule-validator.ts +156 -0
- package/ts/proxies/smart-proxy/certificate-manager.ts +3 -2
- package/ts/proxies/smart-proxy/connection-manager.ts +21 -8
- package/ts/proxies/smart-proxy/http-proxy-bridge.ts +39 -13
- package/ts/proxies/smart-proxy/models/interfaces.ts +0 -1
- package/ts/proxies/smart-proxy/route-connection-handler.ts +88 -16
- package/ts/proxies/smart-proxy/security-manager.ts +98 -86
- package/ts/proxies/smart-proxy/smart-proxy.ts +0 -2
- package/ts/proxies/smart-proxy/tls-manager.ts +1 -37
- package/ts/proxies/smart-proxy/utils/index.ts +3 -5
- package/ts/proxies/smart-proxy/utils/route-helpers/api-helpers.ts +144 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.ts +124 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/http-helpers.ts +40 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/https-helpers.ts +163 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/index.ts +62 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.ts +154 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.ts +202 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/security-helpers.ts +96 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.ts +337 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.ts +98 -0
- package/ts/proxies/smart-proxy/utils/route-helpers.ts +5 -1302
- package/ts/proxies/smart-proxy/utils/route-utils.ts +1 -1
- package/ts/proxies/smart-proxy/utils/route-validator.ts +274 -4
- package/ts/proxies/http-proxy/certificate-manager.ts +0 -244
- 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
|
|
5
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
22
|
-
|
|
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';
|