@robiki/proxy 1.0.3 → 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/README.de.md +3 -3
- package/README.es.md +3 -2
- package/README.ja.md +3 -2
- package/README.md +7 -7
- package/README.pl.md +3 -2
- package/README.ru.md +3 -2
- package/README.zh.md +3 -2
- package/dist/config-Bmt0asXb.d.ts +168 -0
- package/dist/config-C-0wrirG.js +315 -0
- package/dist/config-DtfpOtWI.d.ts +137 -0
- package/dist/index.d.ts +14 -3
- package/dist/index.js +65 -72
- package/dist/utils/config.d.ts +1 -1
- package/dist/utils/config.js +3 -232
- package/package.json +1 -1
package/README.de.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
- **✅ Anforderungsvalidierung**: Benutzerdefinierte Validierungslogik für Authentifizierung, Rate Limiting usw.
|
|
19
19
|
- **🔄 URL-Remapping**: URLs vor der Weiterleitung an Zieldienste transformieren
|
|
20
20
|
- **📦 Duale Nutzung**: Als npm-Paket oder Docker-Container verwenden
|
|
21
|
-
- **⚙️ JavaScript & TypeScript-Konfigurationsunterstützung**: Verwenden Sie `.
|
|
21
|
+
- **⚙️ JavaScript & TypeScript-Konfigurationsunterstützung**: Verwenden Sie `.js` oder `.ts`-Konfigurationsdateien mit Funktionen in Docker
|
|
22
22
|
- **🎯 Multi-Port-Unterstützung**: Gleichzeitiges Lauschen auf mehreren Ports
|
|
23
23
|
- **⚡ Hohe Leistung**: Basiert auf der nativen HTTP/2-Implementierung von Node.js
|
|
24
24
|
|
|
@@ -145,8 +145,8 @@ Einfache deklarative Konfiguration:
|
|
|
145
145
|
Für erweiterte Funktionen wie URL-Remapping und Validierung:
|
|
146
146
|
|
|
147
147
|
```javascript
|
|
148
|
-
// proxy.config.
|
|
149
|
-
|
|
148
|
+
// proxy.config.js
|
|
149
|
+
export default {
|
|
150
150
|
ports: [443, 8080],
|
|
151
151
|
ssl: {
|
|
152
152
|
key: './certs/key.pem',
|
package/README.es.md
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
- **✅ Validación de solicitudes**: Lógica de validación personalizada para autenticación, limitación de velocidad, etc.
|
|
19
19
|
- **🔄 Remapeo de URL**: Transforma URLs antes de reenviarlas a servicios de destino
|
|
20
20
|
- **📦 Uso dual**: Usa como paquete npm o contenedor Docker
|
|
21
|
+
- **⚙️ Soporte de configuración JavaScript y TypeScript**: Usa archivos de configuración `.js` o `.ts` con funciones en Docker
|
|
21
22
|
- **🎯 Soporte multi-puerto**: Escucha en múltiples puertos simultáneamente
|
|
22
23
|
- **⚡ Alto rendimiento**: Construido sobre la implementación nativa HTTP/2 de Node.js
|
|
23
24
|
|
|
@@ -144,8 +145,8 @@ Configuración declarativa simple:
|
|
|
144
145
|
Para características avanzadas como remapeo de URL y validación:
|
|
145
146
|
|
|
146
147
|
```javascript
|
|
147
|
-
// proxy.config.
|
|
148
|
-
|
|
148
|
+
// proxy.config.js
|
|
149
|
+
export default {
|
|
149
150
|
ports: [443, 8080],
|
|
150
151
|
ssl: {
|
|
151
152
|
key: './certs/key.pem',
|
package/README.ja.md
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
- **✅ リクエスト検証**:認証、レート制限などのカスタム検証ロジック
|
|
19
19
|
- **🔄 URL リマッピング**:ターゲットサービスに転送する前に URL を変換
|
|
20
20
|
- **📦 デュアル使用**:npm パッケージまたは Docker コンテナとして使用
|
|
21
|
+
- **⚙️ JavaScript & TypeScript 設定サポート**:Docker で関数を使用した `.js` または `.ts` 設定ファイルを使用
|
|
21
22
|
- **🎯 マルチポートサポート**:複数のポートで同時にリッスン
|
|
22
23
|
- **⚡ 高性能**:Node.js ネイティブ HTTP/2 実装に基づく
|
|
23
24
|
|
|
@@ -144,8 +145,8 @@ docker-compose up -d
|
|
|
144
145
|
URL リマッピングや検証などの高度な機能用:
|
|
145
146
|
|
|
146
147
|
```javascript
|
|
147
|
-
// proxy.config.
|
|
148
|
-
|
|
148
|
+
// proxy.config.js
|
|
149
|
+
export default {
|
|
149
150
|
ports: [443, 8080],
|
|
150
151
|
ssl: {
|
|
151
152
|
key: './certs/key.pem',
|
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
- **✅ Request Validation**: Custom validation logic for authentication, rate limiting, etc.
|
|
19
19
|
- **🔄 URL Remapping**: Transform URLs before forwarding to target services
|
|
20
20
|
- **📦 Dual Usage**: Use as npm package or Docker container
|
|
21
|
-
- **⚙️ JavaScript & TypeScript Config Support**: Use `.
|
|
21
|
+
- **⚙️ JavaScript & TypeScript Config Support**: Use `.js` or `.ts` config files with functions in Docker
|
|
22
22
|
- **🎯 Multi-Port Support**: Listen on multiple ports simultaneously
|
|
23
23
|
- **⚡ High Performance**: Built on Node.js native HTTP/2 implementation
|
|
24
24
|
|
|
@@ -145,8 +145,8 @@ Simple declarative configuration:
|
|
|
145
145
|
For advanced features like URL remapping and validation:
|
|
146
146
|
|
|
147
147
|
```javascript
|
|
148
|
-
// proxy.config.
|
|
149
|
-
|
|
148
|
+
// proxy.config.js
|
|
149
|
+
export default {
|
|
150
150
|
ports: [443, 8080],
|
|
151
151
|
ssl: {
|
|
152
152
|
key: './certs/key.pem',
|
|
@@ -262,17 +262,17 @@ interface RouteConfig {
|
|
|
262
262
|
|
|
263
263
|
## 🐳 Docker Usage
|
|
264
264
|
|
|
265
|
-
Mount your config file (JSON, .
|
|
265
|
+
Mount your config file (JSON, .js, or .ts):
|
|
266
266
|
|
|
267
267
|
```yaml
|
|
268
268
|
services:
|
|
269
269
|
proxy:
|
|
270
270
|
image: robiki/proxy:latest
|
|
271
271
|
volumes:
|
|
272
|
-
- ./proxy.config.
|
|
273
|
-
- ./certs:/
|
|
272
|
+
- ./proxy.config.js:/usr/src/proxy.config.js:ro
|
|
273
|
+
- ./certs:/usr/src/certs:ro
|
|
274
274
|
environment:
|
|
275
|
-
- PROXY_CONFIG=/
|
|
275
|
+
- PROXY_CONFIG=/usr/src/proxy.config.js
|
|
276
276
|
```
|
|
277
277
|
|
|
278
278
|
## 🔐 SSL Certificates
|
package/README.pl.md
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
- **✅ Walidacja żądań**: Niestandardowa logika walidacji dla uwierzytelniania, limitowania żądań itp.
|
|
19
19
|
- **🔄 Przekierowywanie URL**: Transformacja URL przed przekazaniem do usług docelowych
|
|
20
20
|
- **📦 Podwójne użycie**: Użyj jako pakiet npm lub kontener Docker
|
|
21
|
+
- **⚙️ Obsługa konfiguracji JavaScript i TypeScript**: Użyj plików konfiguracyjnych `.js` lub `.ts` z funkcjami w Docker
|
|
21
22
|
- **🎯 Obsługa wielu portów**: Nasłuchiwanie na wielu portach jednocześnie
|
|
22
23
|
- **⚡ Wysoka wydajność**: Zbudowany na natywnej implementacji HTTP/2 w Node.js
|
|
23
24
|
|
|
@@ -144,8 +145,8 @@ Prosta deklaratywna konfiguracja:
|
|
|
144
145
|
Dla zaawansowanych funkcji jak przekierowywanie URL i walidacja:
|
|
145
146
|
|
|
146
147
|
```javascript
|
|
147
|
-
// proxy.config.
|
|
148
|
-
|
|
148
|
+
// proxy.config.js
|
|
149
|
+
export default {
|
|
149
150
|
ports: [443, 8080],
|
|
150
151
|
ssl: {
|
|
151
152
|
key: './certs/key.pem',
|
package/README.ru.md
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
- **✅ Валидация запросов**: Пользовательская логика валидации для аутентификации, ограничения скорости и т.д.
|
|
19
19
|
- **🔄 Переназначение URL**: Преобразование URL перед пересылкой к целевым сервисам
|
|
20
20
|
- **📦 Двойное использование**: Используйте как npm-пакет или Docker-контейнер
|
|
21
|
+
- **⚙️ Поддержка конфигурации JavaScript и TypeScript**: Используйте файлы конфигурации `.js` или `.ts` с функциями в Docker
|
|
21
22
|
- **🎯 Поддержка нескольких портов**: Прослушивание нескольких портов одновременно
|
|
22
23
|
- **⚡ Высокая производительность**: Построен на нативной реализации HTTP/2 в Node.js
|
|
23
24
|
|
|
@@ -144,8 +145,8 @@ docker-compose up -d
|
|
|
144
145
|
Для расширенных функций, таких как переназначение URL и валидация:
|
|
145
146
|
|
|
146
147
|
```javascript
|
|
147
|
-
// proxy.config.
|
|
148
|
-
|
|
148
|
+
// proxy.config.js
|
|
149
|
+
export default {
|
|
149
150
|
ports: [443, 8080],
|
|
150
151
|
ssl: {
|
|
151
152
|
key: './certs/key.pem',
|
package/README.zh.md
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
- **✅ 请求验证**:用于身份验证、速率限制等的自定义验证逻辑
|
|
19
19
|
- **🔄 URL 重映射**:在转发到目标服务之前转换 URL
|
|
20
20
|
- **📦 双重用途**:可作为 npm 包或 Docker 容器使用
|
|
21
|
+
- **⚙️ JavaScript 和 TypeScript 配置支持**:在 Docker 中使用带有函数的 `.js` 或 `.ts` 配置文件
|
|
21
22
|
- **🎯 多端口支持**:同时监听多个端口
|
|
22
23
|
- **⚡ 高性能**:基于 Node.js 原生 HTTP/2 实现
|
|
23
24
|
|
|
@@ -144,8 +145,8 @@ docker-compose up -d
|
|
|
144
145
|
用于高级功能,如 URL 重映射和验证:
|
|
145
146
|
|
|
146
147
|
```javascript
|
|
147
|
-
// proxy.config.
|
|
148
|
-
|
|
148
|
+
// proxy.config.js
|
|
149
|
+
export default {
|
|
149
150
|
ports: [443, 8080],
|
|
150
151
|
ssl: {
|
|
151
152
|
key: './certs/key.pem',
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { OutgoingHttpHeaders, IncomingMessage } from 'node:http';
|
|
2
|
+
import { IncomingHttpHeaders } from 'node:http2';
|
|
3
|
+
import { TLSSocket } from 'node:tls';
|
|
4
|
+
import WebSocket from 'ws';
|
|
5
|
+
|
|
6
|
+
declare enum RequestType {
|
|
7
|
+
API = "api",
|
|
8
|
+
STREAM = "stream",
|
|
9
|
+
WEBSOCKET = "websocket"
|
|
10
|
+
}
|
|
11
|
+
interface TLSWebSocket extends WebSocket {
|
|
12
|
+
_socket: TLSSocket;
|
|
13
|
+
}
|
|
14
|
+
type Router = (req: any, res: any) => void;
|
|
15
|
+
type WebSocketRouter = (req: IncomingMessage, socket: TLSWebSocket, headers: IncomingHttpHeaders) => void;
|
|
16
|
+
type Streamer = (stream: any, headers: any, flags: any) => void;
|
|
17
|
+
interface ForwardValidationResult {
|
|
18
|
+
status: boolean;
|
|
19
|
+
message?: string;
|
|
20
|
+
code?: number;
|
|
21
|
+
headers?: OutgoingHttpHeaders;
|
|
22
|
+
}
|
|
23
|
+
interface ConnectionInfo {
|
|
24
|
+
id: number;
|
|
25
|
+
method: string;
|
|
26
|
+
path: string;
|
|
27
|
+
remoteAddress: string;
|
|
28
|
+
scheme: string;
|
|
29
|
+
authority: string;
|
|
30
|
+
origin: string;
|
|
31
|
+
headers: IncomingHttpHeaders;
|
|
32
|
+
query: URLSearchParams;
|
|
33
|
+
type: RequestType;
|
|
34
|
+
respond: (status: number, headers?: Record<string, string>, body?: string) => void;
|
|
35
|
+
end: (body?: string) => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Route configuration for a specific domain/host
|
|
40
|
+
*/
|
|
41
|
+
interface RouteConfig {
|
|
42
|
+
/** Target host:port to proxy to */
|
|
43
|
+
target: string;
|
|
44
|
+
/** Enable SSL/TLS for the target */
|
|
45
|
+
ssl?: boolean;
|
|
46
|
+
/** Remap the URL path before forwarding */
|
|
47
|
+
remap?: (url: string) => string;
|
|
48
|
+
/** Custom CORS configuration */
|
|
49
|
+
cors?: CorsConfig;
|
|
50
|
+
/** Validation function for this route */
|
|
51
|
+
validate?: (info: ConnectionInfo) => Promise<ForwardValidationResult>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* CORS configuration
|
|
55
|
+
*/
|
|
56
|
+
interface CorsConfig {
|
|
57
|
+
/** Allowed origins (array of strings or '*' for all) */
|
|
58
|
+
origin?: string | string[];
|
|
59
|
+
/** Allowed HTTP methods */
|
|
60
|
+
methods?: string[];
|
|
61
|
+
/** Allowed headers */
|
|
62
|
+
allowedHeaders?: string[];
|
|
63
|
+
/** Exposed headers */
|
|
64
|
+
exposedHeaders?: string[];
|
|
65
|
+
/** Allow credentials */
|
|
66
|
+
credentials?: boolean;
|
|
67
|
+
/** Max age for preflight cache */
|
|
68
|
+
maxAge?: number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* SSL/TLS certificate configuration
|
|
72
|
+
*/
|
|
73
|
+
interface CertificateConfig {
|
|
74
|
+
/** Path to private key file or key content */
|
|
75
|
+
key: string;
|
|
76
|
+
/** Path to certificate file or cert content */
|
|
77
|
+
cert: string;
|
|
78
|
+
/** Path to CA file or CA content */
|
|
79
|
+
ca?: string;
|
|
80
|
+
/** Allow HTTP/1.1 fallback */
|
|
81
|
+
allowHTTP1?: boolean;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Server configuration
|
|
85
|
+
*/
|
|
86
|
+
interface ServerConfig {
|
|
87
|
+
/** SSL/TLS certificate configuration */
|
|
88
|
+
ssl?: CertificateConfig;
|
|
89
|
+
/** Route configurations mapped by host */
|
|
90
|
+
routes: Record<string, RouteConfig>;
|
|
91
|
+
/** Default CORS configuration */
|
|
92
|
+
cors?: CorsConfig;
|
|
93
|
+
/** Global validation function */
|
|
94
|
+
validate?: (info: ConnectionInfo) => Promise<ForwardValidationResult>;
|
|
95
|
+
/** Ports to listen on (defaults to [443, 8080, 9229]) */
|
|
96
|
+
ports?: number[];
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Proxy configuration manager
|
|
100
|
+
*/
|
|
101
|
+
declare class ProxyConfig {
|
|
102
|
+
private config;
|
|
103
|
+
private sslConfig?;
|
|
104
|
+
constructor(config: ServerConfig);
|
|
105
|
+
/**
|
|
106
|
+
* Initialize SSL configuration
|
|
107
|
+
*/
|
|
108
|
+
private initializeSSL;
|
|
109
|
+
/**
|
|
110
|
+
* Get SSL configuration
|
|
111
|
+
*/
|
|
112
|
+
getSSL(): {
|
|
113
|
+
key: Buffer;
|
|
114
|
+
cert: Buffer;
|
|
115
|
+
ca?: Buffer;
|
|
116
|
+
allowHTTP1?: boolean;
|
|
117
|
+
} | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* Get route configuration for a host
|
|
120
|
+
*/
|
|
121
|
+
getRoute(host: string): RouteConfig | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* Get target for a host
|
|
124
|
+
*/
|
|
125
|
+
getTarget(host: string): {
|
|
126
|
+
target: undefined;
|
|
127
|
+
ssl: undefined;
|
|
128
|
+
remap: undefined;
|
|
129
|
+
} | {
|
|
130
|
+
target: string;
|
|
131
|
+
ssl: {
|
|
132
|
+
key: Buffer;
|
|
133
|
+
cert: Buffer;
|
|
134
|
+
ca?: Buffer;
|
|
135
|
+
allowHTTP1?: boolean;
|
|
136
|
+
} | undefined;
|
|
137
|
+
remap: ((url: string) => string) | undefined;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Get CORS headers for a request
|
|
141
|
+
*/
|
|
142
|
+
getCorsHeaders(origin: string, host?: string): OutgoingHttpHeaders;
|
|
143
|
+
/**
|
|
144
|
+
* Validate a request
|
|
145
|
+
*/
|
|
146
|
+
validate(info: ConnectionInfo): Promise<ForwardValidationResult>;
|
|
147
|
+
/**
|
|
148
|
+
* Get ports to listen on
|
|
149
|
+
*/
|
|
150
|
+
getPorts(): number[];
|
|
151
|
+
/**
|
|
152
|
+
* Get the full configuration
|
|
153
|
+
*/
|
|
154
|
+
getConfig(): ServerConfig;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Load configuration with cascading priority (async):
|
|
158
|
+
* 1. Programmatic config (highest priority)
|
|
159
|
+
* 2. Environment variables
|
|
160
|
+
* 3. Config file
|
|
161
|
+
* 4. Defaults (lowest priority)
|
|
162
|
+
*
|
|
163
|
+
* Supports config file types: .json, .ts, .js
|
|
164
|
+
*/
|
|
165
|
+
declare function loadConfig(programmaticConfig?: Partial<ServerConfig>): Promise<ProxyConfig>;
|
|
166
|
+
|
|
167
|
+
export { ProxyConfig as P, RequestType as e, loadConfig as l };
|
|
168
|
+
export type { CorsConfig as C, ForwardValidationResult as F, Router as R, ServerConfig as S, TLSWebSocket as T, WebSocketRouter as W, Streamer as a, RouteConfig as b, CertificateConfig as c, ConnectionInfo as d };
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { stat, readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
|
|
4
|
+
function debug(message, data) {
|
|
5
|
+
if (process.env.DEBUG === "true") {
|
|
6
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
7
|
+
if (data !== void 0) {
|
|
8
|
+
const sanitized = JSON.stringify(data, (_, value) => typeof value === "function" ? "<function>" : value, 2);
|
|
9
|
+
return console.log(`[${timestamp}] [CONFIG] ${message}`, sanitized);
|
|
10
|
+
}
|
|
11
|
+
return console.log(`[${timestamp}] [CONFIG] ${message}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function loadSSLKey(keyConfig) {
|
|
16
|
+
debug(`Loading SSL key from: ${keyConfig.includes("-----BEGIN") ? "inline" : keyConfig}`);
|
|
17
|
+
return keyConfig.includes("-----BEGIN") ? Promise.resolve(Buffer.from(keyConfig)) : readFile(resolve(keyConfig));
|
|
18
|
+
}
|
|
19
|
+
function loadSSLCert(certConfig) {
|
|
20
|
+
debug(`Loading SSL cert from: ${certConfig.includes("-----BEGIN") ? "inline" : certConfig}`);
|
|
21
|
+
return certConfig.includes("-----BEGIN") ? Promise.resolve(Buffer.from(certConfig)) : readFile(resolve(certConfig));
|
|
22
|
+
}
|
|
23
|
+
function loadSSLCA(caConfig) {
|
|
24
|
+
if (!caConfig) {
|
|
25
|
+
debug("Loading SSL CA from: none");
|
|
26
|
+
return Promise.resolve(void 0);
|
|
27
|
+
}
|
|
28
|
+
debug(`Loading SSL CA from: ${caConfig.includes("-----BEGIN") ? "inline" : caConfig}`);
|
|
29
|
+
return caConfig.includes("-----BEGIN") ? Promise.resolve(Buffer.from(caConfig)) : readFile(resolve(caConfig));
|
|
30
|
+
}
|
|
31
|
+
function initializeSSL(sslConfig) {
|
|
32
|
+
debug("Initializing SSL configuration...");
|
|
33
|
+
return Promise.all([loadSSLKey(sslConfig.key), loadSSLCert(sslConfig.cert), loadSSLCA(sslConfig.ca)]).then(([key, cert, ca]) => {
|
|
34
|
+
const loaded = {
|
|
35
|
+
key,
|
|
36
|
+
cert,
|
|
37
|
+
ca,
|
|
38
|
+
allowHTTP1: sslConfig.allowHTTP1 ?? true
|
|
39
|
+
};
|
|
40
|
+
debug("SSL configuration loaded successfully", {
|
|
41
|
+
keySize: key.length,
|
|
42
|
+
certSize: cert.length,
|
|
43
|
+
caSize: ca?.length || 0,
|
|
44
|
+
allowHTTP1: loaded.allowHTTP1
|
|
45
|
+
});
|
|
46
|
+
return loaded;
|
|
47
|
+
}).catch((error) => {
|
|
48
|
+
debug("Failed to load SSL certificates", error);
|
|
49
|
+
console.error("Failed to load SSL certificates:", error);
|
|
50
|
+
throw error;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function getRoute(config, host) {
|
|
54
|
+
if (config.routes[host]) {
|
|
55
|
+
return config.routes[host];
|
|
56
|
+
}
|
|
57
|
+
const hostWithoutPort = host.split(":")[0];
|
|
58
|
+
if (config.routes[hostWithoutPort]) {
|
|
59
|
+
return config.routes[hostWithoutPort];
|
|
60
|
+
}
|
|
61
|
+
for (const [pattern, route] of Object.entries(config.routes)) {
|
|
62
|
+
if (pattern.includes("*")) {
|
|
63
|
+
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
64
|
+
if (regex.test(host) || regex.test(hostWithoutPort)) {
|
|
65
|
+
return route;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return void 0;
|
|
70
|
+
}
|
|
71
|
+
function getTarget(config, sslConfig, host) {
|
|
72
|
+
const route = getRoute(config, host);
|
|
73
|
+
if (!route) {
|
|
74
|
+
return { target: void 0, ssl: void 0, remap: void 0 };
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
target: route.target,
|
|
78
|
+
ssl: route.ssl ? sslConfig : void 0,
|
|
79
|
+
remap: route.remap
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function getCorsHeaders(config, origin, host) {
|
|
83
|
+
const route = host ? getRoute(config, host) : void 0;
|
|
84
|
+
const corsConfig = route?.cors || config.cors;
|
|
85
|
+
if (!corsConfig) {
|
|
86
|
+
return {
|
|
87
|
+
"access-control-allow-origin": origin,
|
|
88
|
+
"access-control-allow-methods": "*",
|
|
89
|
+
"access-control-allow-headers": "*",
|
|
90
|
+
"access-control-allow-credentials": "true"
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const headers = {};
|
|
94
|
+
if (corsConfig.origin === "*") {
|
|
95
|
+
headers["access-control-allow-origin"] = "*";
|
|
96
|
+
} else if (Array.isArray(corsConfig.origin)) {
|
|
97
|
+
if (corsConfig.origin.includes(origin)) {
|
|
98
|
+
headers["access-control-allow-origin"] = origin;
|
|
99
|
+
}
|
|
100
|
+
} else if (corsConfig.origin) {
|
|
101
|
+
headers["access-control-allow-origin"] = corsConfig.origin;
|
|
102
|
+
} else {
|
|
103
|
+
headers["access-control-allow-origin"] = origin;
|
|
104
|
+
}
|
|
105
|
+
if (corsConfig.methods) {
|
|
106
|
+
headers["access-control-allow-methods"] = corsConfig.methods.join(", ");
|
|
107
|
+
} else {
|
|
108
|
+
headers["access-control-allow-methods"] = "*";
|
|
109
|
+
}
|
|
110
|
+
if (corsConfig.allowedHeaders) {
|
|
111
|
+
headers["access-control-allow-headers"] = corsConfig.allowedHeaders.join(", ");
|
|
112
|
+
} else {
|
|
113
|
+
headers["access-control-allow-headers"] = "*";
|
|
114
|
+
}
|
|
115
|
+
if (corsConfig.exposedHeaders) {
|
|
116
|
+
headers["access-control-expose-headers"] = corsConfig.exposedHeaders.join(", ");
|
|
117
|
+
}
|
|
118
|
+
if (corsConfig.credentials !== void 0) {
|
|
119
|
+
headers["access-control-allow-credentials"] = corsConfig.credentials ? "true" : "false";
|
|
120
|
+
} else {
|
|
121
|
+
headers["access-control-allow-credentials"] = "true";
|
|
122
|
+
}
|
|
123
|
+
if (corsConfig.maxAge) {
|
|
124
|
+
headers["access-control-max-age"] = corsConfig.maxAge.toString();
|
|
125
|
+
}
|
|
126
|
+
return headers;
|
|
127
|
+
}
|
|
128
|
+
function validateRequest(config, info) {
|
|
129
|
+
const route = getRoute(config, info.authority);
|
|
130
|
+
if (route?.validate) return route.validate(info);
|
|
131
|
+
if (config.validate) return config.validate(info);
|
|
132
|
+
return Promise.resolve({ status: true });
|
|
133
|
+
}
|
|
134
|
+
function getPorts(config) {
|
|
135
|
+
return config.ports || [443, 8080, 9229];
|
|
136
|
+
}
|
|
137
|
+
function createProxyConfig(config, sslConfig) {
|
|
138
|
+
return {
|
|
139
|
+
config,
|
|
140
|
+
sslConfig,
|
|
141
|
+
getSSL: () => sslConfig,
|
|
142
|
+
getRoute: (host) => getRoute(config, host),
|
|
143
|
+
getTarget: (host) => getTarget(config, sslConfig, host),
|
|
144
|
+
getCorsHeaders: (origin, host) => getCorsHeaders(config, origin, host),
|
|
145
|
+
validate: (info) => validateRequest(config, info),
|
|
146
|
+
getPorts: () => getPorts(config),
|
|
147
|
+
getConfig: () => config
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function deepMerge(...objects) {
|
|
151
|
+
const result = {};
|
|
152
|
+
for (const obj of objects) {
|
|
153
|
+
if (!obj) continue;
|
|
154
|
+
for (const key in obj) {
|
|
155
|
+
if (obj[key] === void 0) continue;
|
|
156
|
+
if (typeof obj[key] === "object" && !Array.isArray(obj[key]) && obj[key] !== null) {
|
|
157
|
+
result[key] = deepMerge(result[key] || {}, obj[key]);
|
|
158
|
+
} else {
|
|
159
|
+
result[key] = obj[key];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
function getConfigFromEnv() {
|
|
166
|
+
debug("Loading configuration from environment variables...");
|
|
167
|
+
const config = {};
|
|
168
|
+
if (process.env.SSL_KEY && process.env.SSL_CERT) {
|
|
169
|
+
debug("Found SSL configuration in environment variables");
|
|
170
|
+
config.ssl = {
|
|
171
|
+
key: process.env.SSL_KEY,
|
|
172
|
+
cert: process.env.SSL_CERT,
|
|
173
|
+
ca: process.env.SSL_CA,
|
|
174
|
+
allowHTTP1: process.env.SSL_ALLOW_HTTP1 === "true"
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (process.env.CORS_ORIGIN) {
|
|
178
|
+
debug("Found CORS configuration in environment variables", {
|
|
179
|
+
origin: process.env.CORS_ORIGIN,
|
|
180
|
+
methods: process.env.CORS_METHODS,
|
|
181
|
+
headers: process.env.CORS_HEADERS,
|
|
182
|
+
credentials: process.env.CORS_CREDENTIALS
|
|
183
|
+
});
|
|
184
|
+
config.cors = {
|
|
185
|
+
origin: process.env.CORS_ORIGIN === "*" ? "*" : process.env.CORS_ORIGIN.split(","),
|
|
186
|
+
methods: process.env.CORS_METHODS?.split(","),
|
|
187
|
+
allowedHeaders: process.env.CORS_HEADERS?.split(","),
|
|
188
|
+
credentials: process.env.CORS_CREDENTIALS === "true"
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
if (Object.keys(config).length === 0) {
|
|
192
|
+
debug("No configuration found in environment variables");
|
|
193
|
+
}
|
|
194
|
+
return config;
|
|
195
|
+
}
|
|
196
|
+
async function getConfigFromFile() {
|
|
197
|
+
const configPath = process.env.PROXY_CONFIG || "./proxy.config.json";
|
|
198
|
+
const resolvedPath = resolve(configPath);
|
|
199
|
+
debug(`Looking for config file at: ${resolvedPath}`);
|
|
200
|
+
try {
|
|
201
|
+
const stats = await stat(resolvedPath);
|
|
202
|
+
debug(`Config file found (${stats.size} bytes)`);
|
|
203
|
+
if (resolvedPath.endsWith(".json")) {
|
|
204
|
+
debug("Loading JSON config file...");
|
|
205
|
+
const configFile2 = await readFile(resolvedPath, "utf-8");
|
|
206
|
+
const config2 = JSON.parse(configFile2);
|
|
207
|
+
debug("JSON config loaded successfully", {
|
|
208
|
+
routes: Object.keys(config2.routes || {}),
|
|
209
|
+
hasCors: !!config2.cors,
|
|
210
|
+
hasSsl: !!config2.ssl,
|
|
211
|
+
ports: config2.ports
|
|
212
|
+
});
|
|
213
|
+
return config2;
|
|
214
|
+
}
|
|
215
|
+
if (resolvedPath.endsWith(".ts") || resolvedPath.endsWith(".js")) {
|
|
216
|
+
const fileType = resolvedPath.endsWith(".ts") ? "TypeScript" : "JavaScript";
|
|
217
|
+
debug(`Loading ${fileType} config file...`);
|
|
218
|
+
const configModule = await import(`file://${resolvedPath}`);
|
|
219
|
+
const config2 = configModule.default || configModule;
|
|
220
|
+
debug(`${fileType} config loaded successfully`, {
|
|
221
|
+
routes: Object.keys(config2.routes || {}),
|
|
222
|
+
hasCors: !!config2.cors,
|
|
223
|
+
hasSsl: !!config2.ssl,
|
|
224
|
+
ports: config2.ports
|
|
225
|
+
});
|
|
226
|
+
return config2;
|
|
227
|
+
}
|
|
228
|
+
debug("Loading config as JSON (no extension match)...");
|
|
229
|
+
const configFile = await readFile(resolvedPath, "utf-8");
|
|
230
|
+
const config = JSON.parse(configFile);
|
|
231
|
+
debug("Config loaded successfully", {
|
|
232
|
+
routes: Object.keys(config.routes || {}),
|
|
233
|
+
hasCors: !!config.cors,
|
|
234
|
+
hasSsl: !!config.ssl,
|
|
235
|
+
ports: config.ports
|
|
236
|
+
});
|
|
237
|
+
return config;
|
|
238
|
+
} catch (error) {
|
|
239
|
+
if (error.code === "ENOENT") {
|
|
240
|
+
debug(`Config file not found: ${resolvedPath}`);
|
|
241
|
+
} else {
|
|
242
|
+
debug(`Error loading config file: ${error.message}`);
|
|
243
|
+
}
|
|
244
|
+
return {};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function loadDefaults() {
|
|
248
|
+
debug("==========================================");
|
|
249
|
+
debug("Starting configuration loading process...");
|
|
250
|
+
debug("==========================================");
|
|
251
|
+
debug("Step 1: Loading default configuration...");
|
|
252
|
+
const defaults = {
|
|
253
|
+
routes: {},
|
|
254
|
+
cors: {
|
|
255
|
+
origin: "*",
|
|
256
|
+
credentials: true
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
debug("Defaults loaded", defaults);
|
|
260
|
+
return defaults;
|
|
261
|
+
}
|
|
262
|
+
function loadAndMergeFileConfig(defaults) {
|
|
263
|
+
debug("Step 2: Loading configuration from file...");
|
|
264
|
+
return getConfigFromFile().then((fileConfig) => {
|
|
265
|
+
if (Object.keys(fileConfig).length > 0) {
|
|
266
|
+
debug("File config loaded", fileConfig);
|
|
267
|
+
} else {
|
|
268
|
+
debug("No file configuration loaded");
|
|
269
|
+
}
|
|
270
|
+
return deepMerge(defaults, fileConfig);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
function mergeEnvConfig(config) {
|
|
274
|
+
debug("Step 3: Loading configuration from environment...");
|
|
275
|
+
const envConfig = getConfigFromEnv();
|
|
276
|
+
return deepMerge(config, envConfig);
|
|
277
|
+
}
|
|
278
|
+
function mergeProgrammaticConfig(config, programmaticConfig) {
|
|
279
|
+
if (programmaticConfig && Object.keys(programmaticConfig).length > 0) {
|
|
280
|
+
debug("Step 4: Programmatic configuration provided", programmaticConfig);
|
|
281
|
+
return deepMerge(config, programmaticConfig);
|
|
282
|
+
}
|
|
283
|
+
return config;
|
|
284
|
+
}
|
|
285
|
+
function logMergedConfig(merged) {
|
|
286
|
+
debug("Step 5: Final merged configuration", {
|
|
287
|
+
routes: Object.keys(merged.routes || {}),
|
|
288
|
+
ports: merged.ports,
|
|
289
|
+
hasCors: !!merged.cors,
|
|
290
|
+
hasSsl: !!merged.ssl,
|
|
291
|
+
hasValidate: !!merged.validate
|
|
292
|
+
});
|
|
293
|
+
return merged;
|
|
294
|
+
}
|
|
295
|
+
function initializeSSLIfConfigured(config) {
|
|
296
|
+
debug("Step 6: Checking SSL configuration...");
|
|
297
|
+
if (!config.ssl) {
|
|
298
|
+
debug("No SSL configuration provided");
|
|
299
|
+
return Promise.resolve({ config, sslConfig: void 0 });
|
|
300
|
+
}
|
|
301
|
+
return initializeSSL(config.ssl).then((sslConfig) => ({ config, sslConfig }));
|
|
302
|
+
}
|
|
303
|
+
function createFinalConfig({ config, sslConfig }) {
|
|
304
|
+
debug("Step 7: Creating ProxyConfig object...");
|
|
305
|
+
const proxyConfig = createProxyConfig(config, sslConfig);
|
|
306
|
+
debug("==========================================");
|
|
307
|
+
debug("Configuration loading completed successfully");
|
|
308
|
+
debug("==========================================");
|
|
309
|
+
return proxyConfig;
|
|
310
|
+
}
|
|
311
|
+
function loadConfig(programmaticConfig) {
|
|
312
|
+
return Promise.resolve(loadDefaults()).then((defaults) => loadAndMergeFileConfig(defaults)).then((config) => mergeEnvConfig(config)).then((config) => mergeProgrammaticConfig(config, programmaticConfig)).then((merged) => logMergedConfig(merged)).then((merged) => initializeSSLIfConfigured(merged)).then((result) => createFinalConfig(result));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export { debug as d, loadConfig as l };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { OutgoingHttpHeaders, IncomingMessage } from 'node:http';
|
|
2
|
+
import { IncomingHttpHeaders } from 'node:http2';
|
|
3
|
+
import { TLSSocket } from 'node:tls';
|
|
4
|
+
import WebSocket from 'ws';
|
|
5
|
+
|
|
6
|
+
declare enum RequestType {
|
|
7
|
+
API = "api",
|
|
8
|
+
STREAM = "stream",
|
|
9
|
+
WEBSOCKET = "websocket"
|
|
10
|
+
}
|
|
11
|
+
interface TLSWebSocket extends WebSocket {
|
|
12
|
+
_socket: TLSSocket;
|
|
13
|
+
}
|
|
14
|
+
type Router = (req: any, res: any) => void;
|
|
15
|
+
type WebSocketRouter = (req: IncomingMessage, socket: TLSWebSocket, headers: IncomingHttpHeaders) => void;
|
|
16
|
+
type Streamer = (stream: any, headers: any, flags: any) => void;
|
|
17
|
+
interface ForwardValidationResult {
|
|
18
|
+
status: boolean;
|
|
19
|
+
message?: string;
|
|
20
|
+
code?: number;
|
|
21
|
+
headers?: OutgoingHttpHeaders;
|
|
22
|
+
}
|
|
23
|
+
interface ConnectionInfo {
|
|
24
|
+
id: number;
|
|
25
|
+
method: string;
|
|
26
|
+
path: string;
|
|
27
|
+
remoteAddress: string;
|
|
28
|
+
scheme: string;
|
|
29
|
+
authority: string;
|
|
30
|
+
origin: string;
|
|
31
|
+
headers: IncomingHttpHeaders;
|
|
32
|
+
query: URLSearchParams;
|
|
33
|
+
type: RequestType;
|
|
34
|
+
respond: (status: number, headers?: Record<string, string>, body?: string) => void;
|
|
35
|
+
end: (body?: string) => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Route configuration for a specific domain/host
|
|
40
|
+
*/
|
|
41
|
+
interface RouteConfig {
|
|
42
|
+
/** Target host:port to proxy to */
|
|
43
|
+
target: string;
|
|
44
|
+
/** Enable SSL/TLS for the target */
|
|
45
|
+
ssl?: boolean;
|
|
46
|
+
/** Remap the URL path before forwarding */
|
|
47
|
+
remap?: (url: string) => string;
|
|
48
|
+
/** Custom CORS configuration */
|
|
49
|
+
cors?: CorsConfig;
|
|
50
|
+
/** Validation function for this route */
|
|
51
|
+
validate?: (info: ConnectionInfo) => Promise<ForwardValidationResult>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* CORS configuration
|
|
55
|
+
*/
|
|
56
|
+
interface CorsConfig {
|
|
57
|
+
/** Allowed origins (array of strings or '*' for all) */
|
|
58
|
+
origin?: string | string[];
|
|
59
|
+
/** Allowed HTTP methods */
|
|
60
|
+
methods?: string[];
|
|
61
|
+
/** Allowed headers */
|
|
62
|
+
allowedHeaders?: string[];
|
|
63
|
+
/** Exposed headers */
|
|
64
|
+
exposedHeaders?: string[];
|
|
65
|
+
/** Allow credentials */
|
|
66
|
+
credentials?: boolean;
|
|
67
|
+
/** Max age for preflight cache */
|
|
68
|
+
maxAge?: number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* SSL/TLS certificate configuration
|
|
72
|
+
*/
|
|
73
|
+
interface CertificateConfig {
|
|
74
|
+
/** Path to private key file or key content */
|
|
75
|
+
key: string;
|
|
76
|
+
/** Path to certificate file or cert content */
|
|
77
|
+
cert: string;
|
|
78
|
+
/** Path to CA file or CA content */
|
|
79
|
+
ca?: string;
|
|
80
|
+
/** Allow HTTP/1.1 fallback */
|
|
81
|
+
allowHTTP1?: boolean;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Server configuration
|
|
85
|
+
*/
|
|
86
|
+
interface ServerConfig {
|
|
87
|
+
/** SSL/TLS certificate configuration */
|
|
88
|
+
ssl?: CertificateConfig;
|
|
89
|
+
/** Route configurations mapped by host */
|
|
90
|
+
routes: Record<string, RouteConfig>;
|
|
91
|
+
/** Default CORS configuration */
|
|
92
|
+
cors?: CorsConfig;
|
|
93
|
+
/** Global validation function */
|
|
94
|
+
validate?: (info: ConnectionInfo) => Promise<ForwardValidationResult>;
|
|
95
|
+
/** Ports to listen on (defaults to [443, 8080, 9229]) */
|
|
96
|
+
ports?: number[];
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Processed SSL configuration with loaded certificates
|
|
100
|
+
*/
|
|
101
|
+
interface LoadedSSLConfig {
|
|
102
|
+
key: Buffer;
|
|
103
|
+
cert: Buffer;
|
|
104
|
+
ca?: Buffer;
|
|
105
|
+
allowHTTP1: boolean;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Proxy configuration object with loaded SSL
|
|
109
|
+
*/
|
|
110
|
+
interface ProxyConfig {
|
|
111
|
+
config: ServerConfig;
|
|
112
|
+
sslConfig?: LoadedSSLConfig;
|
|
113
|
+
getSSL: () => LoadedSSLConfig | undefined;
|
|
114
|
+
getRoute: (host: string) => RouteConfig | undefined;
|
|
115
|
+
getTarget: (host: string) => {
|
|
116
|
+
target: string | undefined;
|
|
117
|
+
ssl: LoadedSSLConfig | undefined;
|
|
118
|
+
remap: ((url: string) => string) | undefined;
|
|
119
|
+
};
|
|
120
|
+
getCorsHeaders: (origin: string, host?: string) => OutgoingHttpHeaders;
|
|
121
|
+
validate: (info: ConnectionInfo) => Promise<ForwardValidationResult>;
|
|
122
|
+
getPorts: () => number[];
|
|
123
|
+
getConfig: () => ServerConfig;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Load configuration with cascading priority using promise chain:
|
|
127
|
+
* 1. Programmatic config (highest priority)
|
|
128
|
+
* 2. Environment variables
|
|
129
|
+
* 3. Config file
|
|
130
|
+
* 4. Defaults (lowest priority)
|
|
131
|
+
*
|
|
132
|
+
* Supports config file types: .json, .ts, .js
|
|
133
|
+
*/
|
|
134
|
+
declare function loadConfig(programmaticConfig?: Partial<ServerConfig>): Promise<ProxyConfig>;
|
|
135
|
+
|
|
136
|
+
export { RequestType as e, loadConfig as l };
|
|
137
|
+
export type { CorsConfig as C, ForwardValidationResult as F, LoadedSSLConfig as L, ProxyConfig as P, Router as R, ServerConfig as S, TLSWebSocket as T, WebSocketRouter as W, Streamer as a, RouteConfig as b, CertificateConfig as c, ConnectionInfo as d };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as ProxyConfig, T as TLSWebSocket, S as ServerConfig, R as Router, a as Streamer, W as WebSocketRouter } from './config-
|
|
2
|
-
export { c as CertificateConfig, d as ConnectionInfo, C as CorsConfig, F as ForwardValidationResult, e as RequestType, b as RouteConfig, l as loadConfig } from './config-
|
|
1
|
+
import { P as ProxyConfig, T as TLSWebSocket, S as ServerConfig, R as Router, a as Streamer, W as WebSocketRouter } from './config-DtfpOtWI.js';
|
|
2
|
+
export { c as CertificateConfig, d as ConnectionInfo, C as CorsConfig, F as ForwardValidationResult, e as RequestType, b as RouteConfig, l as loadConfig } from './config-DtfpOtWI.js';
|
|
3
3
|
import { IncomingMessage, ServerResponse, IncomingHttpHeaders as IncomingHttpHeaders$1 } from 'node:http';
|
|
4
4
|
import { ServerHttp2Stream, IncomingHttpHeaders } from 'node:http2';
|
|
5
5
|
import 'node:tls';
|
|
@@ -33,11 +33,22 @@ declare class ProxyServer {
|
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* Create and start a proxy server
|
|
36
|
-
* Supports
|
|
36
|
+
* Supports config file types: .json, .ts, .js
|
|
37
|
+
*
|
|
38
|
+
* Promise chain:
|
|
39
|
+
* 1. Load configuration from file/env/programmatic
|
|
40
|
+
* 2. Initialize SSL certificates
|
|
41
|
+
* 3. Create proxy server instance
|
|
42
|
+
* 4. Start listening on configured ports
|
|
37
43
|
*/
|
|
38
44
|
declare function createProxy(config?: Partial<ServerConfig>): Promise<ProxyServer>;
|
|
39
45
|
/**
|
|
40
46
|
* Create a proxy server with custom handlers
|
|
47
|
+
*
|
|
48
|
+
* Promise chain:
|
|
49
|
+
* 1. Load configuration from file/env/programmatic
|
|
50
|
+
* 2. Initialize SSL certificates
|
|
51
|
+
* 3. Create servers with custom handlers
|
|
41
52
|
*/
|
|
42
53
|
declare function createCustomProxy(config: Partial<ServerConfig> | undefined, handlers: {
|
|
43
54
|
rest?: Router;
|
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 './
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
400
|
+
debug("WebSocket proxy error:", error);
|
|
409
401
|
socket.close();
|
|
410
402
|
});
|
|
411
403
|
};
|
|
@@ -486,58 +478,59 @@ class ProxyServer {
|
|
|
486
478
|
}
|
|
487
479
|
}
|
|
488
480
|
async function createProxy(config) {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
481
|
+
return loadConfig(config).then((proxyConfig) => {
|
|
482
|
+
const proxy = new ProxyServer(proxyConfig);
|
|
483
|
+
return proxy.start().then(() => proxy);
|
|
484
|
+
});
|
|
493
485
|
}
|
|
494
486
|
async function createCustomProxy(config, handlers) {
|
|
495
|
-
const servers = [];
|
|
496
487
|
console.log("STARTING CUSTOM PROXY SERVER....");
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
console.log("Custom proxy server started successfully");
|
|
524
|
-
return {
|
|
525
|
-
getConfig: () => proxyConfig,
|
|
526
|
-
start: async () => {
|
|
527
|
-
},
|
|
528
|
-
stop: async () => {
|
|
529
|
-
await Promise.all(
|
|
530
|
-
servers.map(
|
|
531
|
-
(server) => new Promise((resolve, reject) => {
|
|
532
|
-
server.close((err) => {
|
|
533
|
-
if (err) reject(err);
|
|
534
|
-
else resolve();
|
|
535
|
-
});
|
|
536
|
-
})
|
|
537
|
-
)
|
|
538
|
-
);
|
|
488
|
+
return loadConfig(config).then((proxyConfig) => {
|
|
489
|
+
const servers = [];
|
|
490
|
+
const ssl = proxyConfig.getSSL();
|
|
491
|
+
const ports = proxyConfig.getPorts();
|
|
492
|
+
const boundRestHandler = (req, res) => {
|
|
493
|
+
if (req.url === "/robiki-proxy/health" && req.method === "GET") {
|
|
494
|
+
res.writeHead(200, { "Content-Type": "text/plain" });
|
|
495
|
+
res.end("OK");
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
return restAPIProxyHandler(req, res, proxyConfig);
|
|
499
|
+
};
|
|
500
|
+
const boundStreamHandler = (stream, headers) => streamAPIProxyHandler(stream, headers, proxyConfig);
|
|
501
|
+
const boundWebsocketHandler = (req, socket, headers) => websocketAPIProxyHandler(req, socket, headers, proxyConfig);
|
|
502
|
+
for (const port of ports) {
|
|
503
|
+
let server;
|
|
504
|
+
if (ssl) {
|
|
505
|
+
server = http2(handlers.rest || boundRestHandler, handlers.stream || boundStreamHandler, {
|
|
506
|
+
...ssl,
|
|
507
|
+
port
|
|
508
|
+
});
|
|
509
|
+
} else {
|
|
510
|
+
server = http(handlers.rest || boundRestHandler, { port });
|
|
511
|
+
}
|
|
512
|
+
websocket(server, handlers.websocket || boundWebsocketHandler, (info) => proxyConfig.validate(info));
|
|
513
|
+
servers.push(server);
|
|
539
514
|
}
|
|
540
|
-
|
|
515
|
+
console.log("Custom proxy server started successfully");
|
|
516
|
+
return {
|
|
517
|
+
getConfig: () => proxyConfig,
|
|
518
|
+
start: async () => {
|
|
519
|
+
},
|
|
520
|
+
stop: async () => {
|
|
521
|
+
await Promise.all(
|
|
522
|
+
servers.map(
|
|
523
|
+
(server) => new Promise((resolve, reject) => {
|
|
524
|
+
server.close((err) => {
|
|
525
|
+
if (err) reject(err);
|
|
526
|
+
else resolve();
|
|
527
|
+
});
|
|
528
|
+
})
|
|
529
|
+
)
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
});
|
|
541
534
|
}
|
|
542
535
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
543
536
|
const setupErrorHandlers = () => {
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -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 } from '../config-
|
|
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';
|
package/dist/utils/config.js
CHANGED
|
@@ -1,232 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
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 loadTypeScriptConfig(resolvedPath) {
|
|
179
|
-
try {
|
|
180
|
-
const { register } = require("tsx/cjs/api");
|
|
181
|
-
const unregister = register();
|
|
182
|
-
try {
|
|
183
|
-
delete require.cache[resolvedPath];
|
|
184
|
-
const configModule = require(resolvedPath);
|
|
185
|
-
return configModule.default || configModule;
|
|
186
|
-
} finally {
|
|
187
|
-
unregister();
|
|
188
|
-
}
|
|
189
|
-
} catch (error) {
|
|
190
|
-
console.warn("Failed to load TypeScript config file:", error);
|
|
191
|
-
console.warn("Make sure tsx is installed: npm install tsx or yarn add tsx");
|
|
192
|
-
return {};
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
async function getConfigFromFile() {
|
|
196
|
-
const configPath = process.env.PROXY_CONFIG || "./proxy.config.json";
|
|
197
|
-
try {
|
|
198
|
-
const resolvedPath = resolve(configPath);
|
|
199
|
-
if (resolvedPath.endsWith(".ts")) {
|
|
200
|
-
return loadTypeScriptConfig(resolvedPath);
|
|
201
|
-
}
|
|
202
|
-
if (resolvedPath.endsWith(".js") || resolvedPath.endsWith(".mjs")) {
|
|
203
|
-
const fileUrl = `file://${resolvedPath}`;
|
|
204
|
-
const configModule = await import(fileUrl);
|
|
205
|
-
return configModule.default || configModule;
|
|
206
|
-
}
|
|
207
|
-
if (resolvedPath.endsWith(".cjs")) {
|
|
208
|
-
delete require.cache[resolvedPath];
|
|
209
|
-
const configModule = require(resolvedPath);
|
|
210
|
-
return configModule.default || configModule;
|
|
211
|
-
}
|
|
212
|
-
const configFile = readFileSync(resolvedPath, "utf-8");
|
|
213
|
-
return JSON.parse(configFile);
|
|
214
|
-
} catch (error) {
|
|
215
|
-
return {};
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
async function loadConfig(programmaticConfig) {
|
|
219
|
-
const defaults = {
|
|
220
|
-
routes: {},
|
|
221
|
-
cors: {
|
|
222
|
-
origin: "*",
|
|
223
|
-
credentials: true
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
const fileConfig = await getConfigFromFile();
|
|
227
|
-
const envConfig = getConfigFromEnv();
|
|
228
|
-
const merged = deepMerge(defaults, fileConfig, envConfig, programmaticConfig || {});
|
|
229
|
-
return new ProxyConfig(merged);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
export { ProxyConfig, loadConfig };
|
|
1
|
+
import 'node:fs/promises';
|
|
2
|
+
import 'node:path';
|
|
3
|
+
export { l as loadConfig } from '../config-C-0wrirG.js';
|
package/package.json
CHANGED