@lopatnov/express-reverse-proxy 4.0.0 → 5.0.1
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/LICENSE +192 -192
- package/README.md +658 -17
- package/package.json +14 -9
- package/server-config.schema.json +321 -0
- package/server.js +255 -10
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lopatnov/express-reverse-proxy",
|
|
3
3
|
"email": "oleksandr@lopatnov.cv.ua",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "5.0.1",
|
|
5
5
|
"description": "Node.js CLI tool to serve static front-end files with a reverse proxy for back-end APIs",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": "lopatnov",
|
|
8
8
|
"main": "server.js",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": "./server.js",
|
|
11
|
-
"./hot-reload-client": "./hot-reload-client.js"
|
|
11
|
+
"./hot-reload-client": "./hot-reload-client.js",
|
|
12
|
+
"./server-config.schema.json": "./server-config.schema.json"
|
|
12
13
|
},
|
|
13
14
|
"bin": {
|
|
14
15
|
"express-reverse-proxy": "./server.js",
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
"server.js",
|
|
62
63
|
"hot-reload-client.js",
|
|
63
64
|
"ecosystem.config.cjs",
|
|
65
|
+
"server-config.schema.json",
|
|
64
66
|
"README.md",
|
|
65
67
|
"LICENSE"
|
|
66
68
|
],
|
|
@@ -68,18 +70,21 @@
|
|
|
68
70
|
"access": "public"
|
|
69
71
|
},
|
|
70
72
|
"dependencies": {
|
|
71
|
-
"compression": "^1.
|
|
72
|
-
"cors": "^2.8.
|
|
73
|
+
"compression": "^1.8.1",
|
|
74
|
+
"cors": "^2.8.6",
|
|
73
75
|
"express": "^5.2.1",
|
|
76
|
+
"express-basic-auth": "^1.2.1",
|
|
74
77
|
"express-http-proxy": "^2.1.2",
|
|
75
|
-
"
|
|
78
|
+
"express-rate-limit": "^8.3.0",
|
|
79
|
+
"helmet": "^8.1.0",
|
|
76
80
|
"morgan": "^1.10.1",
|
|
81
|
+
"multer": "^2.1.1",
|
|
77
82
|
"pm2": "^6.0.14",
|
|
78
|
-
"response-time": "^2.3.
|
|
79
|
-
"serve-favicon": "^2.5.
|
|
83
|
+
"response-time": "^2.3.4",
|
|
84
|
+
"serve-favicon": "^2.5.1"
|
|
80
85
|
},
|
|
81
86
|
"devDependencies": {
|
|
82
|
-
"@biomejs/biome": "^2.4.
|
|
83
|
-
"cypress": "^15.
|
|
87
|
+
"@biomejs/biome": "^2.4.6",
|
|
88
|
+
"cypress": "^15.11.0"
|
|
84
89
|
}
|
|
85
90
|
}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://unpkg.com/@lopatnov/express-reverse-proxy/server-config.schema.json",
|
|
4
|
+
"title": "express-reverse-proxy server config",
|
|
5
|
+
"description": "Configuration file for @lopatnov/express-reverse-proxy",
|
|
6
|
+
"oneOf": [
|
|
7
|
+
{ "$ref": "#/definitions/siteConfig" },
|
|
8
|
+
{
|
|
9
|
+
"type": "array",
|
|
10
|
+
"items": { "$ref": "#/definitions/siteConfig" },
|
|
11
|
+
"minItems": 1
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"definitions": {
|
|
15
|
+
"siteConfig": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"additionalProperties": false,
|
|
18
|
+
"properties": {
|
|
19
|
+
"port": {
|
|
20
|
+
"type": "integer",
|
|
21
|
+
"minimum": 1,
|
|
22
|
+
"maximum": 65535,
|
|
23
|
+
"default": 8000,
|
|
24
|
+
"description": "TCP port the server listens on."
|
|
25
|
+
},
|
|
26
|
+
"host": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"description": "Virtual host to match against the HTTP Host header. Use \"*\" or omit for catch-all."
|
|
29
|
+
},
|
|
30
|
+
"folders": {
|
|
31
|
+
"$ref": "#/definitions/foldersOption",
|
|
32
|
+
"description": "Static file serving. String, array, or path-mapped object."
|
|
33
|
+
},
|
|
34
|
+
"proxy": {
|
|
35
|
+
"$ref": "#/definitions/proxyOption",
|
|
36
|
+
"description": "Reverse proxy routes. String, array, object, or object with array targets for round-robin."
|
|
37
|
+
},
|
|
38
|
+
"headers": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"additionalProperties": { "type": "string" },
|
|
41
|
+
"description": "Custom response headers added to every response."
|
|
42
|
+
},
|
|
43
|
+
"unhandled": {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"description": "Catch-all response sent when no route matches, keyed by Accept type.",
|
|
46
|
+
"additionalProperties": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"properties": {
|
|
49
|
+
"status": { "type": "integer" },
|
|
50
|
+
"send": { "type": "string" },
|
|
51
|
+
"file": { "type": "string" },
|
|
52
|
+
"headers": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"additionalProperties": { "type": "string" }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"logging": {
|
|
60
|
+
"description": "Request logging. true = console (dev format), false = disabled, object = custom.",
|
|
61
|
+
"oneOf": [
|
|
62
|
+
{ "type": "boolean" },
|
|
63
|
+
{
|
|
64
|
+
"type": "object",
|
|
65
|
+
"additionalProperties": false,
|
|
66
|
+
"properties": {
|
|
67
|
+
"format": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"enum": ["combined", "common", "dev", "short", "tiny"],
|
|
70
|
+
"default": "combined",
|
|
71
|
+
"description": "Morgan format string."
|
|
72
|
+
},
|
|
73
|
+
"file": {
|
|
74
|
+
"type": "string",
|
|
75
|
+
"description": "Path to log file (relative to config file). Appends if exists."
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
"ssl": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"required": ["key", "cert"],
|
|
84
|
+
"additionalProperties": false,
|
|
85
|
+
"description": "Enable HTTPS. Paths are relative to the config file.",
|
|
86
|
+
"properties": {
|
|
87
|
+
"key": { "type": "string", "description": "Path to private key PEM file." },
|
|
88
|
+
"cert": { "type": "string", "description": "Path to certificate PEM file." },
|
|
89
|
+
"ca": { "type": "string", "description": "Path to CA bundle PEM file (optional)." },
|
|
90
|
+
"redirect": {
|
|
91
|
+
"type": "integer",
|
|
92
|
+
"minimum": 1,
|
|
93
|
+
"maximum": 65535,
|
|
94
|
+
"description": "HTTP port to listen on and redirect (301) to HTTPS."
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"hotReload": {
|
|
99
|
+
"type": "boolean",
|
|
100
|
+
"description": "Watch folders and push SSE reload events to connected browsers."
|
|
101
|
+
},
|
|
102
|
+
"compression": {
|
|
103
|
+
"description": "Enable gzip/deflate response compression via the compression package.",
|
|
104
|
+
"oneOf": [{ "type": "boolean" }, { "type": "object" }]
|
|
105
|
+
},
|
|
106
|
+
"helmet": {
|
|
107
|
+
"description": "Enable security headers via the helmet package.",
|
|
108
|
+
"oneOf": [{ "type": "boolean" }, { "type": "object" }]
|
|
109
|
+
},
|
|
110
|
+
"cors": {
|
|
111
|
+
"description": "Enable CORS headers and preflight handling via the cors package.",
|
|
112
|
+
"oneOf": [
|
|
113
|
+
{ "type": "boolean" },
|
|
114
|
+
{
|
|
115
|
+
"type": "object",
|
|
116
|
+
"properties": {
|
|
117
|
+
"origin": {},
|
|
118
|
+
"methods": {},
|
|
119
|
+
"allowedHeaders": {},
|
|
120
|
+
"exposedHeaders": {},
|
|
121
|
+
"credentials": { "type": "boolean" },
|
|
122
|
+
"maxAge": { "type": "integer" }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
"favicon": {
|
|
128
|
+
"type": "string",
|
|
129
|
+
"description": "Path to favicon file (relative to config file)."
|
|
130
|
+
},
|
|
131
|
+
"responseTime": {
|
|
132
|
+
"description": "Add X-Response-Time header via the response-time package.",
|
|
133
|
+
"oneOf": [{ "type": "boolean" }, { "type": "object" }]
|
|
134
|
+
},
|
|
135
|
+
"rateLimit": {
|
|
136
|
+
"type": "object",
|
|
137
|
+
"description": "Rate limiting via express-rate-limit. Returns 429 when exceeded.",
|
|
138
|
+
"properties": {
|
|
139
|
+
"windowMs": { "type": "integer", "description": "Time window in milliseconds." },
|
|
140
|
+
"limit": { "type": "integer", "description": "Max requests per window." }
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"basicAuth": {
|
|
144
|
+
"type": "object",
|
|
145
|
+
"description": "HTTP Basic Auth via express-basic-auth.",
|
|
146
|
+
"required": ["users"],
|
|
147
|
+
"properties": {
|
|
148
|
+
"users": {
|
|
149
|
+
"type": "object",
|
|
150
|
+
"additionalProperties": { "type": "string" },
|
|
151
|
+
"description": "Map of username → password."
|
|
152
|
+
},
|
|
153
|
+
"challenge": { "type": "boolean" },
|
|
154
|
+
"realm": { "type": "string" }
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"cgi": {
|
|
158
|
+
"description": "CGI/1.1 script execution. String (dir path), object, or array.",
|
|
159
|
+
"oneOf": [
|
|
160
|
+
{ "type": "string" },
|
|
161
|
+
{ "$ref": "#/definitions/cgiEntry" },
|
|
162
|
+
{
|
|
163
|
+
"type": "array",
|
|
164
|
+
"items": { "$ref": "#/definitions/cgiEntry" }
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
},
|
|
168
|
+
"upload": {
|
|
169
|
+
"description": "File upload endpoint via multer. String (dir path), object, or array.",
|
|
170
|
+
"oneOf": [
|
|
171
|
+
{ "type": "string" },
|
|
172
|
+
{ "$ref": "#/definitions/uploadEntry" },
|
|
173
|
+
{
|
|
174
|
+
"type": "array",
|
|
175
|
+
"items": { "$ref": "#/definitions/uploadEntry" }
|
|
176
|
+
}
|
|
177
|
+
]
|
|
178
|
+
},
|
|
179
|
+
"healthCheck": {
|
|
180
|
+
"description": "Expose a health check endpoint returning {status, uptime, timestamp}.",
|
|
181
|
+
"oneOf": [
|
|
182
|
+
{ "type": "boolean" },
|
|
183
|
+
{
|
|
184
|
+
"type": "object",
|
|
185
|
+
"additionalProperties": false,
|
|
186
|
+
"properties": {
|
|
187
|
+
"path": {
|
|
188
|
+
"type": "string",
|
|
189
|
+
"default": "/__health__",
|
|
190
|
+
"description": "URL path for the health check endpoint."
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
},
|
|
196
|
+
"redirects": {
|
|
197
|
+
"description": "URL redirect rules. Object map or array of {from, to, status} entries.",
|
|
198
|
+
"oneOf": [
|
|
199
|
+
{
|
|
200
|
+
"type": "object",
|
|
201
|
+
"additionalProperties": {
|
|
202
|
+
"oneOf": [
|
|
203
|
+
{ "type": "string" },
|
|
204
|
+
{
|
|
205
|
+
"type": "object",
|
|
206
|
+
"required": ["to"],
|
|
207
|
+
"properties": {
|
|
208
|
+
"to": { "type": "string" },
|
|
209
|
+
"status": { "type": "integer", "enum": [301, 302, 307, 308], "default": 301 }
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"type": "array",
|
|
217
|
+
"items": {
|
|
218
|
+
"type": "object",
|
|
219
|
+
"required": ["from", "to"],
|
|
220
|
+
"additionalProperties": false,
|
|
221
|
+
"properties": {
|
|
222
|
+
"from": { "type": "string", "description": "Source path." },
|
|
223
|
+
"to": { "type": "string", "description": "Destination path or URL." },
|
|
224
|
+
"status": { "type": "integer", "enum": [301, 302, 307, 308], "default": 301 }
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
]
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
"foldersOption": {
|
|
233
|
+
"oneOf": [
|
|
234
|
+
{ "type": "string" },
|
|
235
|
+
{
|
|
236
|
+
"type": "array",
|
|
237
|
+
"items": { "$ref": "#/definitions/foldersOption" }
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"type": "object",
|
|
241
|
+
"additionalProperties": { "$ref": "#/definitions/foldersOption" }
|
|
242
|
+
}
|
|
243
|
+
]
|
|
244
|
+
},
|
|
245
|
+
"proxyOption": {
|
|
246
|
+
"oneOf": [
|
|
247
|
+
{ "type": "string" },
|
|
248
|
+
{
|
|
249
|
+
"type": "array",
|
|
250
|
+
"items": {
|
|
251
|
+
"oneOf": [{ "type": "string" }, { "$ref": "#/definitions/proxyTarget" }]
|
|
252
|
+
},
|
|
253
|
+
"description": "Round-robin load balancing across multiple targets."
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"type": "object",
|
|
257
|
+
"additionalProperties": {
|
|
258
|
+
"oneOf": [
|
|
259
|
+
{ "type": "string" },
|
|
260
|
+
{
|
|
261
|
+
"type": "array",
|
|
262
|
+
"items": { "type": "string" },
|
|
263
|
+
"description": "Round-robin targets."
|
|
264
|
+
}
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
]
|
|
269
|
+
},
|
|
270
|
+
"proxyTarget": {
|
|
271
|
+
"type": "string"
|
|
272
|
+
},
|
|
273
|
+
"cgiEntry": {
|
|
274
|
+
"type": "object",
|
|
275
|
+
"additionalProperties": false,
|
|
276
|
+
"properties": {
|
|
277
|
+
"path": { "type": "string", "default": "/cgi-bin", "description": "URL prefix." },
|
|
278
|
+
"dir": {
|
|
279
|
+
"type": "string",
|
|
280
|
+
"description": "Directory with CGI scripts (relative to config file)."
|
|
281
|
+
},
|
|
282
|
+
"extensions": {
|
|
283
|
+
"type": "array",
|
|
284
|
+
"items": { "type": "string" },
|
|
285
|
+
"default": [".cgi", ".pl", ".py", ".sh"],
|
|
286
|
+
"description": "File extensions to treat as CGI scripts."
|
|
287
|
+
},
|
|
288
|
+
"interpreters": {
|
|
289
|
+
"type": "object",
|
|
290
|
+
"additionalProperties": { "type": "string" },
|
|
291
|
+
"description": "Map of extension → interpreter command. Required on Windows."
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
"uploadEntry": {
|
|
296
|
+
"type": "object",
|
|
297
|
+
"additionalProperties": false,
|
|
298
|
+
"properties": {
|
|
299
|
+
"path": { "type": "string", "default": "/upload", "description": "URL prefix." },
|
|
300
|
+
"dir": {
|
|
301
|
+
"type": "string",
|
|
302
|
+
"description": "Directory to save uploaded files (relative to config file)."
|
|
303
|
+
},
|
|
304
|
+
"maxFileSize": {
|
|
305
|
+
"type": "integer",
|
|
306
|
+
"description": "Max file size in bytes. Returns 413 when exceeded."
|
|
307
|
+
},
|
|
308
|
+
"maxFiles": { "type": "integer", "description": "Max number of files per request." },
|
|
309
|
+
"allowedTypes": {
|
|
310
|
+
"type": "array",
|
|
311
|
+
"items": { "type": "string" },
|
|
312
|
+
"description": "MIME type whitelist. Returns 400 for other types."
|
|
313
|
+
},
|
|
314
|
+
"fieldName": {
|
|
315
|
+
"type": "string",
|
|
316
|
+
"description": "Accept only files from this form field name."
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|