@robiki/proxy 1.0.2 → 1.0.4

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/dist/index.js CHANGED
@@ -5,8 +5,8 @@ import { createServer as createServer$1, request } from 'node:https';
5
5
  import { constants, createSecureServer, createServer, connect } from 'node:http2';
6
6
  import { exec } from 'node:child_process';
7
7
  import { WebSocketServer, WebSocket } from 'ws';
8
- import { loadConfig } from './utils/config.js';
9
- import 'node:fs';
8
+ import { d as debug, l as loadConfig } from './config-C-0wrirG.js';
9
+ import 'node:fs/promises';
10
10
  import 'node:path';
11
11
 
12
12
  const num = (seed) => {
@@ -224,7 +224,6 @@ function isMediaFile(path) {
224
224
  });
225
225
  }
226
226
 
227
- const DEBUG$2 = process.env.DEBUG === "true";
228
227
  const restAPIProxyHandler = async (req, res, config) => {
229
228
  const { target, ssl, remap } = config.getTarget(req.headers.host || req.headers[":authority"]?.toString() || "");
230
229
  if (req.httpVersion === "2.0" && ssl) return;
@@ -233,12 +232,12 @@ const restAPIProxyHandler = async (req, res, config) => {
233
232
  res.end("Not Found");
234
233
  return;
235
234
  }
236
- if (DEBUG$2) console.log("HTTP1 rest proxy", `${ssl ? "https" : "http"}://${target}${req.url}`, req.headers.host);
235
+ debug(`HTTP1 rest proxy for ${req.headers.host}`, `${ssl ? "https" : "http"}://${target}${req.url}`);
237
236
  if (remap) req.url = remap(req.url || "");
238
237
  const requestFn = ssl ? request : request$1;
239
238
  const headers = req.httpVersion === "2.0" ? http2HeadersToHttp1Headers(req.headers) : req.headers;
240
239
  const method = req.httpVersion === "2.0" ? req.headers[":method"]?.toString() : req.method;
241
- if (DEBUG$2) console.log("Proxy Request::", req.url, method, headers);
240
+ debug(`Proxy Request :: ${req.url} ${method} ::`, headers);
242
241
  const proxy = requestFn(
243
242
  `${ssl ? "https" : "http"}://${target}${req.url || ""}`,
244
243
  {
@@ -263,13 +262,13 @@ const restAPIProxyHandler = async (req, res, config) => {
263
262
  }
264
263
  });
265
264
  proxyRes.on("error", (error) => {
266
- if (DEBUG$2) console.error("Proxy response error:", error);
265
+ debug("Proxy response error:", error);
267
266
  if (!res.destroyed) res.destroy(error);
268
267
  });
269
268
  }
270
269
  );
271
270
  proxy.on("error", (error) => {
272
- if (DEBUG$2) console.error("Proxy request error:", error);
271
+ debug("Proxy request error:", error);
273
272
  if (!res.headersSent) {
274
273
  res.writeHead(502, { "Content-Type": "text/plain" });
275
274
  res.end("Bad Gateway");
@@ -286,12 +285,11 @@ const restAPIProxyHandler = async (req, res, config) => {
286
285
  }
287
286
  });
288
287
  req.on("error", (error) => {
289
- if (DEBUG$2) console.error("Client request error:", error);
288
+ debug("Client request error:", error);
290
289
  if (!proxy.destroyed) proxy.destroy(error);
291
290
  });
292
291
  };
293
292
 
294
- const DEBUG$1 = process.env.DEBUG === "true";
295
293
  const streamAPIProxyHandler = async (stream, headers, config) => {
296
294
  const { target, ssl, remap } = config.getTarget(headers[":authority"] || "");
297
295
  if (!ssl) return;
@@ -299,14 +297,9 @@ const streamAPIProxyHandler = async (stream, headers, config) => {
299
297
  stream.destroy(new Error("Not Found"));
300
298
  return;
301
299
  }
302
- if (DEBUG$1)
303
- console.log(
304
- "HTTP2 stream proxy",
305
- `${ssl ? "https" : "http"}://${target}${headers[":path"]}`,
306
- headers[":authority"]
307
- );
300
+ debug(`HTTP2 stream proxy for ${headers[":authority"]}`, `${ssl ? "https" : "http"}://${target}${headers[":path"]}`);
308
301
  if (remap) headers[":path"] = remap(headers[":path"] || "");
309
- if (DEBUG$1) console.log("Proxy Request::", headers[":path"]);
302
+ debug("Proxy Request::", headers[":path"]);
310
303
  const proxy = connect(`https://${target}${headers[":path"]}`, {
311
304
  ...ssl,
312
305
  rejectUnauthorized: false
@@ -315,7 +308,7 @@ const streamAPIProxyHandler = async (stream, headers, config) => {
315
308
  const request = proxy.request(headers);
316
309
  request.on("response", (headerResponse) => {
317
310
  if (!stream.writableEnded && !stream.closed && !stream.destroyed) {
318
- if (DEBUG$1) console.log("Proxy Response::", headerResponse[":status"], `for ${headers[":path"]}`);
311
+ debug(`Proxy Response for ${headers[":path"]}::`, headerResponse[":status"]);
319
312
  if (headers[":path"] && isMediaFile(headers[":path"])) {
320
313
  headerResponse["cache-control"] = `public, max-age=${day()}`;
321
314
  }
@@ -342,7 +335,7 @@ const streamAPIProxyHandler = async (stream, headers, config) => {
342
335
  if (!stream.closed && !stream.destroyed) stream.close();
343
336
  });
344
337
  stream.on("error", (error) => {
345
- if (DEBUG$1) console.error("HTTP2 stream proxy error:", error);
338
+ debug("HTTP2 stream proxy error:", error);
346
339
  if (!request.destroyed) request.destroy(error);
347
340
  if (!proxy.closed) proxy.close();
348
341
  });
@@ -360,28 +353,27 @@ const streamAPIProxyHandler = async (stream, headers, config) => {
360
353
  if (!stream.closed && !stream.destroyed) stream.close();
361
354
  });
362
355
  request.on("error", (error) => {
363
- if (DEBUG$1) console.error("HTTP2 request proxy error:", error);
356
+ debug("HTTP2 request proxy error:", error);
364
357
  if (!stream.destroyed) stream.destroy(error);
365
358
  return !proxy.closed && proxy.close();
366
359
  });
367
360
  proxy.on("timeout", () => {
368
- if (DEBUG$1) console.error("HTTP/2 client timeout");
361
+ debug("HTTP/2 client timeout");
369
362
  if (!stream.destroyed) stream.destroy(new Error("HTTP/2 client timeout"));
370
363
  });
371
364
  });
372
365
  proxy.on("error", (error) => {
373
- if (DEBUG$1) console.error("HTTP2 proxy connection error:", error);
366
+ debug("HTTP2 proxy connection error:", error);
374
367
  if (!stream.destroyed) {
375
368
  stream.destroy(error);
376
369
  }
377
370
  });
378
371
  };
379
372
 
380
- const DEBUG = process.env.DEBUG === "true";
381
373
  const websocketAPIProxyHandler = async (req, socket, headers, config) => {
382
374
  const { target, ssl, remap } = config.getTarget(req.headers.host || "");
383
375
  if (!target) return socket.close();
384
- if (DEBUG) console.log("HTTP2 websocket proxy", `${ssl ? "https" : "http"}://${target}${req.url}`, headers.host);
376
+ debug(`HTTP2 websocket proxy for ${headers.host}`, `${ssl ? "https" : "http"}://${target}${req.url}`);
385
377
  if (remap) req.url = remap(req.url || "");
386
378
  const proxy = new WebSocket(
387
379
  `${ssl ? "wss" : "ws"}://${target}${req.url || ""}`,
@@ -405,7 +397,7 @@ const websocketAPIProxyHandler = async (req, socket, headers, config) => {
405
397
  proxy.on("close", () => socket.close());
406
398
  socket.on("close", () => proxy.close());
407
399
  proxy.on("error", (error) => {
408
- if (DEBUG) console.error("WebSocket proxy error:", error);
400
+ debug("WebSocket proxy error:", error);
409
401
  socket.close();
410
402
  });
411
403
  };
@@ -485,30 +477,18 @@ class ProxyServer {
485
477
  return this.config;
486
478
  }
487
479
  }
488
- function createProxy(config) {
489
- const createProxyInstance = () => {
490
- return new ProxyServer(loadConfig(config));
491
- };
492
- const startProxy = (proxy) => {
480
+ async function createProxy(config) {
481
+ return loadConfig(config).then((proxyConfig) => {
482
+ const proxy = new ProxyServer(proxyConfig);
493
483
  return proxy.start().then(() => proxy);
494
- };
495
- return Promise.resolve().then(() => createProxyInstance()).then((proxy) => startProxy(proxy));
484
+ });
496
485
  }
497
- function createCustomProxy(config, handlers) {
498
- const servers = [];
499
- const initializeConfig = () => {
500
- const proxyConfig = loadConfig(config);
501
- return {
502
- ssl: proxyConfig.getSSL(),
503
- ports: proxyConfig.getPorts(),
504
- proxyConfig
505
- };
506
- };
507
- const logStartup = (cfg) => {
508
- console.log("STARTING CUSTOM PROXY SERVER....");
509
- return cfg;
510
- };
511
- const createServers = ({ ssl, ports, proxyConfig }) => {
486
+ async function createCustomProxy(config, handlers) {
487
+ console.log("STARTING CUSTOM PROXY SERVER....");
488
+ return loadConfig(config).then((proxyConfig) => {
489
+ const servers = [];
490
+ const ssl = proxyConfig.getSSL();
491
+ const ports = proxyConfig.getPorts();
512
492
  const boundRestHandler = (req, res) => {
513
493
  if (req.url === "/robiki-proxy/health" && req.method === "GET") {
514
494
  res.writeHead(200, { "Content-Type": "text/plain" });
@@ -532,9 +512,6 @@ function createCustomProxy(config, handlers) {
532
512
  websocket(server, handlers.websocket || boundWebsocketHandler, (info) => proxyConfig.validate(info));
533
513
  servers.push(server);
534
514
  }
535
- return proxyConfig;
536
- };
537
- const createProxyInstance = (proxyConfig) => {
538
515
  console.log("Custom proxy server started successfully");
539
516
  return {
540
517
  getConfig: () => proxyConfig,
@@ -553,12 +530,7 @@ function createCustomProxy(config, handlers) {
553
530
  );
554
531
  }
555
532
  };
556
- };
557
- const handleError = (error) => {
558
- console.error("Failed to start custom proxy server:", error);
559
- throw error;
560
- };
561
- return Promise.resolve().then(() => initializeConfig()).then((cfg) => logStartup(cfg)).then((cfg) => createServers(cfg)).then((proxyConfig) => createProxyInstance(proxyConfig)).catch((error) => handleError(error));
533
+ });
562
534
  }
563
535
  if (import.meta.url === `file://${process.argv[1]}`) {
564
536
  const setupErrorHandlers = () => {
@@ -569,15 +541,15 @@ if (import.meta.url === `file://${process.argv[1]}`) {
569
541
  console.log("UNHANDLED REJECTION: ", reason, promise);
570
542
  });
571
543
  };
572
- const startProxyServer = () => {
573
- return createProxy();
544
+ const startProxyServer = async () => {
545
+ return await createProxy();
574
546
  };
575
547
  const handleStartupError = (error) => {
576
548
  console.error("Failed to start proxy server:", error);
577
549
  process.exit(1);
578
550
  };
579
551
  setupErrorHandlers();
580
- Promise.resolve().then(() => startProxyServer()).catch((error) => handleStartupError(error));
552
+ startProxyServer().catch((error) => handleStartupError(error));
581
553
  }
582
554
 
583
555
  export { ProxyServer, RequestType, createCustomProxy, createProxy, loadConfig, restAPIProxyHandler, streamAPIProxyHandler, websocketAPIProxyHandler };
@@ -1,5 +1,5 @@
1
1
  import 'node:http';
2
- export { c as CertificateConfig, C as CorsConfig, P as ProxyConfig, b as RouteConfig, S as ServerConfig, l as loadConfig, g as loadConfigFromEnv, f as loadConfigFromFile } from '../config-CeJ1tf8T.js';
2
+ export { c as CertificateConfig, C as CorsConfig, L as LoadedSSLConfig, P as ProxyConfig, b as RouteConfig, S as ServerConfig, l as loadConfig } from '../config-DtfpOtWI.js';
3
3
  import 'node:http2';
4
4
  import 'node:tls';
5
5
  import 'ws';
@@ -1,215 +1,3 @@
1
- import { readFileSync } from 'node:fs';
2
- import { resolve } from 'node:path';
3
-
4
- class ProxyConfig {
5
- config;
6
- sslConfig;
7
- constructor(config) {
8
- this.config = config;
9
- this.initializeSSL();
10
- }
11
- /**
12
- * Initialize SSL configuration
13
- */
14
- initializeSSL() {
15
- if (!this.config.ssl) return;
16
- try {
17
- const key = this.config.ssl.key.includes("-----BEGIN") ? Buffer.from(this.config.ssl.key) : readFileSync(resolve(this.config.ssl.key));
18
- const cert = this.config.ssl.cert.includes("-----BEGIN") ? Buffer.from(this.config.ssl.cert) : readFileSync(resolve(this.config.ssl.cert));
19
- const ca = this.config.ssl.ca ? this.config.ssl.ca.includes("-----BEGIN") ? Buffer.from(this.config.ssl.ca) : readFileSync(resolve(this.config.ssl.ca)) : void 0;
20
- this.sslConfig = {
21
- key,
22
- cert,
23
- ca,
24
- allowHTTP1: this.config.ssl.allowHTTP1 ?? true
25
- };
26
- } catch (error) {
27
- console.error("Failed to load SSL certificates:", error);
28
- throw error;
29
- }
30
- }
31
- /**
32
- * Get SSL configuration
33
- */
34
- getSSL() {
35
- return this.sslConfig;
36
- }
37
- /**
38
- * Get route configuration for a host
39
- */
40
- getRoute(host) {
41
- if (this.config.routes[host]) {
42
- return this.config.routes[host];
43
- }
44
- const hostWithoutPort = host.split(":")[0];
45
- if (this.config.routes[hostWithoutPort]) {
46
- return this.config.routes[hostWithoutPort];
47
- }
48
- for (const [pattern, route] of Object.entries(this.config.routes)) {
49
- if (pattern.includes("*")) {
50
- const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
51
- if (regex.test(host) || regex.test(hostWithoutPort)) {
52
- return route;
53
- }
54
- }
55
- }
56
- return void 0;
57
- }
58
- /**
59
- * Get target for a host
60
- */
61
- getTarget(host) {
62
- const route = this.getRoute(host);
63
- if (!route) {
64
- return { target: void 0, ssl: void 0, remap: void 0 };
65
- }
66
- return {
67
- target: route.target,
68
- ssl: route.ssl ? this.sslConfig : void 0,
69
- remap: route.remap
70
- };
71
- }
72
- /**
73
- * Get CORS headers for a request
74
- */
75
- getCorsHeaders(origin, host) {
76
- const route = host ? this.getRoute(host) : void 0;
77
- const corsConfig = route?.cors || this.config.cors;
78
- if (!corsConfig) {
79
- return {
80
- "access-control-allow-origin": origin,
81
- "access-control-allow-methods": "*",
82
- "access-control-allow-headers": "*",
83
- "access-control-allow-credentials": "true"
84
- };
85
- }
86
- const headers = {};
87
- if (corsConfig.origin === "*") {
88
- headers["access-control-allow-origin"] = "*";
89
- } else if (Array.isArray(corsConfig.origin)) {
90
- if (corsConfig.origin.includes(origin)) {
91
- headers["access-control-allow-origin"] = origin;
92
- }
93
- } else if (corsConfig.origin) {
94
- headers["access-control-allow-origin"] = corsConfig.origin;
95
- } else {
96
- headers["access-control-allow-origin"] = origin;
97
- }
98
- if (corsConfig.methods) {
99
- headers["access-control-allow-methods"] = corsConfig.methods.join(", ");
100
- } else {
101
- headers["access-control-allow-methods"] = "*";
102
- }
103
- if (corsConfig.allowedHeaders) {
104
- headers["access-control-allow-headers"] = corsConfig.allowedHeaders.join(", ");
105
- } else {
106
- headers["access-control-allow-headers"] = "*";
107
- }
108
- if (corsConfig.exposedHeaders) {
109
- headers["access-control-expose-headers"] = corsConfig.exposedHeaders.join(", ");
110
- }
111
- if (corsConfig.credentials !== void 0) {
112
- headers["access-control-allow-credentials"] = corsConfig.credentials ? "true" : "false";
113
- } else {
114
- headers["access-control-allow-credentials"] = "true";
115
- }
116
- if (corsConfig.maxAge) {
117
- headers["access-control-max-age"] = corsConfig.maxAge.toString();
118
- }
119
- return headers;
120
- }
121
- /**
122
- * Validate a request
123
- */
124
- async validate(info) {
125
- const route = this.getRoute(info.authority);
126
- if (route?.validate) return route.validate(info);
127
- if (this.config.validate) return this.config.validate(info);
128
- return { status: true };
129
- }
130
- /**
131
- * Get ports to listen on
132
- */
133
- getPorts() {
134
- return this.config.ports || [443, 8080, 9229];
135
- }
136
- /**
137
- * Get the full configuration
138
- */
139
- getConfig() {
140
- return this.config;
141
- }
142
- }
143
- function deepMerge(...objects) {
144
- const result = {};
145
- for (const obj of objects) {
146
- if (!obj) continue;
147
- for (const key in obj) {
148
- if (obj[key] === void 0) continue;
149
- if (typeof obj[key] === "object" && !Array.isArray(obj[key]) && obj[key] !== null) {
150
- result[key] = deepMerge(result[key] || {}, obj[key]);
151
- } else {
152
- result[key] = obj[key];
153
- }
154
- }
155
- }
156
- return result;
157
- }
158
- function getConfigFromEnv() {
159
- const config = {};
160
- if (process.env.SSL_KEY && process.env.SSL_CERT) {
161
- config.ssl = {
162
- key: process.env.SSL_KEY,
163
- cert: process.env.SSL_CERT,
164
- ca: process.env.SSL_CA,
165
- allowHTTP1: process.env.SSL_ALLOW_HTTP1 === "true"
166
- };
167
- }
168
- if (process.env.CORS_ORIGIN) {
169
- config.cors = {
170
- origin: process.env.CORS_ORIGIN === "*" ? "*" : process.env.CORS_ORIGIN.split(","),
171
- methods: process.env.CORS_METHODS?.split(","),
172
- allowedHeaders: process.env.CORS_HEADERS?.split(","),
173
- credentials: process.env.CORS_CREDENTIALS === "true"
174
- };
175
- }
176
- return config;
177
- }
178
- function getConfigFromFile() {
179
- const configPath = process.env.PROXY_CONFIG || "./proxy.config.json";
180
- try {
181
- const configFile = readFileSync(resolve(configPath), "utf-8");
182
- return JSON.parse(configFile);
183
- } catch (error) {
184
- return {};
185
- }
186
- }
187
- function loadConfig(programmaticConfig) {
188
- const defaults = {
189
- routes: {},
190
- cors: {
191
- origin: "*",
192
- credentials: true
193
- }
194
- };
195
- const fileConfig = getConfigFromFile();
196
- const envConfig = getConfigFromEnv();
197
- const merged = deepMerge(defaults, fileConfig, envConfig, programmaticConfig || {});
198
- return new ProxyConfig(merged);
199
- }
200
- function loadConfigFromFile(path) {
201
- try {
202
- const configFile = readFileSync(resolve(path), "utf-8");
203
- const config = JSON.parse(configFile);
204
- return new ProxyConfig(config);
205
- } catch (error) {
206
- console.error("Failed to load configuration file:", error);
207
- throw error;
208
- }
209
- }
210
- function loadConfigFromEnv() {
211
- const config = getConfigFromEnv();
212
- return new ProxyConfig(config);
213
- }
214
-
215
- export { ProxyConfig, loadConfig, loadConfigFromEnv, loadConfigFromFile };
1
+ import 'node:fs/promises';
2
+ import 'node:path';
3
+ export { l as loadConfig } from '../config-C-0wrirG.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robiki/proxy",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "A flexible HTTP/2 proxy server with WebSocket support, configurable routing, CORS, and validation. Use as npm package or Docker container.",
5
5
  "keywords": [
6
6
  "proxy",
@@ -32,10 +32,6 @@
32
32
  "import": "./dist/index.js",
33
33
  "types": "./dist/index.d.ts"
34
34
  },
35
- "./proxy": {
36
- "import": "./dist/proxy.js",
37
- "types": "./dist/proxy.d.ts"
38
- },
39
35
  "./config": {
40
36
  "import": "./dist/utils/config.js",
41
37
  "types": "./dist/utils/config.d.ts"
@@ -78,6 +74,7 @@
78
74
  },
79
75
  "dependencies": {
80
76
  "@types/ws": "8.18.1",
77
+ "tsx": "4.19.2",
81
78
  "ws": "8.19.0"
82
79
  },
83
80
  "engines": {