egg-http-proxy-plus 2.0.0 → 2.1.0
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.md +228 -82
- package/app/middleware/httpProxyPlus.js +110 -51
- package/app.js +1 -1
- package/config/config.default.js +19 -1
- package/index.d.ts +108 -0
- package/package.json +68 -105
package/README.md
CHANGED
|
@@ -2,150 +2,296 @@
|
|
|
2
2
|
|
|
3
3
|
# egg-http-proxy-plus
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[![NPM version][npm-image]][npm-url]
|
|
6
|
+
[![npm download][download-image]][download-url]
|
|
7
|
+
[![License][license-image]][license-url]
|
|
8
|
+
[![Node.js Version][node-image]][node-url]
|
|
9
|
+
[](https://github.com/saqqdy/egg-http-proxy-plus/actions)
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
> A powerful HTTP proxy middleware plugin for Egg.js with TypeScript support
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
**中文文档** | [English](#english)
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
Built on top of [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware).
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
**[📝 Changelog](CHANGELOG.md)** | **[📦 Examples](examples/proxy-demo/)** | **[🐛 Issues](https://github.com/saqqdy/egg-http-proxy-plus/issues)**
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
[![Codacy Badge][codacy-image]][codacy-url]
|
|
17
|
-
[![build status][travis-image]][travis-url]
|
|
18
|
-
[![Test coverage][codecov-image]][codecov-url]
|
|
19
|
-
[![npm download][download-image]][download-url]
|
|
20
|
-
[![License][license-image]][license-url]
|
|
19
|
+
---
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
## Features
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
| Feature | Description |
|
|
24
|
+
|---------|-------------|
|
|
25
|
+
| 📁 File Upload | Built-in multipart/form-data support |
|
|
26
|
+
| 🔧 Custom Matching | Function-based path matching |
|
|
27
|
+
| 🔄 ctx Passing | Access Egg.js context in callbacks |
|
|
28
|
+
| 📘 TypeScript | Built-in type definitions |
|
|
29
|
+
| 🔌 WebSocket | WS proxy support |
|
|
30
|
+
| ✏️ Path Rewrite | Flexible rewrite rules |
|
|
31
|
+
| 🛡️ Error Handling | Custom error callbacks |
|
|
25
32
|
|
|
26
|
-
|
|
33
|
+
## Requirements
|
|
27
34
|
|
|
28
|
-
|
|
35
|
+
| Dependency | Version |
|
|
36
|
+
|------------|---------|
|
|
37
|
+
| Node.js | >= 14.0.0 |
|
|
38
|
+
| Egg.js | >= 3.0.0 |
|
|
29
39
|
|
|
30
|
-
|
|
31
|
-
# use npm
|
|
32
|
-
$ npm i egg-http-proxy-plus --save
|
|
40
|
+
## Installation
|
|
33
41
|
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
```bash
|
|
43
|
+
npm i egg-http-proxy-plus
|
|
44
|
+
# or
|
|
45
|
+
pnpm add egg-http-proxy-plus
|
|
46
|
+
# or
|
|
47
|
+
yarn add egg-http-proxy-plus
|
|
36
48
|
```
|
|
37
49
|
|
|
38
|
-
##
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
### 1. Enable Plugin
|
|
39
53
|
|
|
40
54
|
```js
|
|
41
|
-
//
|
|
55
|
+
// config/plugin.js
|
|
42
56
|
exports.httpProxyPlus = {
|
|
43
57
|
enable: true,
|
|
44
58
|
package: 'egg-http-proxy-plus'
|
|
45
59
|
}
|
|
46
60
|
```
|
|
47
61
|
|
|
48
|
-
|
|
62
|
+
### 2. Configure Proxy
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
// config/config.default.js
|
|
66
|
+
exports.httpProxyPlus = {
|
|
67
|
+
'/api': 'http://backend.example.com'
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 3. Test
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
curl http://localhost:7001/api/users
|
|
75
|
+
# Proxies to http://backend.example.com/api/users
|
|
76
|
+
```
|
|
49
77
|
|
|
50
|
-
|
|
78
|
+
## Configuration Examples
|
|
79
|
+
|
|
80
|
+
### Basic
|
|
51
81
|
|
|
52
82
|
```js
|
|
53
|
-
//
|
|
83
|
+
// Object form
|
|
54
84
|
exports.httpProxyPlus = {
|
|
55
|
-
|
|
85
|
+
'/api': 'http://backend.example.com',
|
|
86
|
+
'/v1': {
|
|
87
|
+
target: 'http://backend.example.com',
|
|
88
|
+
pathRewrite: { '^/v1': '/api/v1' }
|
|
89
|
+
}
|
|
56
90
|
}
|
|
57
|
-
|
|
91
|
+
|
|
92
|
+
// Array form
|
|
58
93
|
exports.httpProxyPlus = [
|
|
59
|
-
|
|
60
|
-
origin: '/
|
|
61
|
-
options: 'http://www.example.org'
|
|
62
|
-
}
|
|
94
|
+
{ origin: '/api', options: 'http://backend.example.com' },
|
|
95
|
+
{ origin: '/v1', options: { target: 'http://backend.example.com' } }
|
|
63
96
|
]
|
|
64
97
|
```
|
|
65
98
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
If you don't want `/api` to be passed along, we need to rewrite the path:
|
|
99
|
+
### Path Rewrite
|
|
69
100
|
|
|
70
101
|
```js
|
|
71
|
-
// {app_root}/config/config.default.js
|
|
72
102
|
exports.httpProxyPlus = {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
103
|
+
'/api': {
|
|
104
|
+
target: 'http://backend.example.com',
|
|
105
|
+
pathRewrite: { '^/api': '' } // Remove /api prefix
|
|
106
|
+
}
|
|
77
107
|
}
|
|
78
|
-
//
|
|
108
|
+
// /api/users -> http://backend.example.com/users
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Custom Matching
|
|
112
|
+
|
|
113
|
+
```js
|
|
79
114
|
exports.httpProxyPlus = [
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
115
|
+
{
|
|
116
|
+
// Match GET requests only
|
|
117
|
+
origin(pathname, req) {
|
|
118
|
+
return pathname.startsWith('/api') && req.method === 'GET'
|
|
119
|
+
},
|
|
120
|
+
options: { target: 'http://backend.example.com' }
|
|
86
121
|
}
|
|
87
|
-
}
|
|
88
122
|
]
|
|
89
123
|
```
|
|
90
124
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
For full control you can provide a custom function to determine which requests should be proxied or not.
|
|
125
|
+
### Add Auth Header
|
|
94
126
|
|
|
95
127
|
```js
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
128
|
+
exports.httpProxyPlus = {
|
|
129
|
+
'/api': {
|
|
130
|
+
target: 'http://backend.example.com',
|
|
131
|
+
onProxyReq(proxyReq, req, res, ctx) {
|
|
132
|
+
const token = ctx.cookies.get('access_token')
|
|
133
|
+
if (token) {
|
|
134
|
+
proxyReq.setHeader('Authorization', `Bearer ${token}`)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
103
138
|
}
|
|
104
|
-
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Response Modification
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
exports.httpProxyPlus = {
|
|
146
|
+
'/api': {
|
|
147
|
+
target: 'http://backend.example.com',
|
|
148
|
+
onProxyRes(proxyRes, req, res, ctx) {
|
|
149
|
+
proxyRes.headers['x-proxy-by'] = 'egg-http-proxy-plus'
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
105
153
|
```
|
|
106
154
|
|
|
107
|
-
|
|
155
|
+
### Error Handling
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
exports.httpProxyPlus = {
|
|
159
|
+
'/api': {
|
|
160
|
+
target: 'http://backend.example.com',
|
|
161
|
+
onError(err, req, res) {
|
|
162
|
+
res.writeHead(502, { 'Content-Type': 'application/json' })
|
|
163
|
+
res.end(JSON.stringify({
|
|
164
|
+
code: 502,
|
|
165
|
+
message: 'Bad Gateway',
|
|
166
|
+
error: err.message
|
|
167
|
+
}))
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
108
172
|
|
|
109
|
-
|
|
173
|
+
### WebSocket Proxy
|
|
110
174
|
|
|
111
175
|
```js
|
|
112
|
-
// {app_root}/config/config.default.js
|
|
113
176
|
exports.httpProxyPlus = {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (req.method.toLowerCase() === 'post') {
|
|
118
|
-
const token = ctx.cookies.get('access_token')
|
|
119
|
-
token && proxyReq.setHeader('authorization', token)
|
|
120
|
-
}
|
|
177
|
+
'/ws': {
|
|
178
|
+
target: 'ws://backend.example.com',
|
|
179
|
+
ws: true
|
|
121
180
|
}
|
|
122
|
-
}
|
|
123
181
|
}
|
|
124
182
|
```
|
|
125
183
|
|
|
126
|
-
|
|
184
|
+
### Multiple Services
|
|
127
185
|
|
|
128
|
-
|
|
186
|
+
```js
|
|
187
|
+
exports.httpProxyPlus = {
|
|
188
|
+
'/api/users': 'http://users-service:3001',
|
|
189
|
+
'/api/orders': 'http://orders-service:3002',
|
|
190
|
+
'/api/products': 'http://products-service:3003'
|
|
191
|
+
}
|
|
192
|
+
```
|
|
129
193
|
|
|
130
|
-
|
|
194
|
+
## API Reference
|
|
195
|
+
|
|
196
|
+
### ProxyConfigItem
|
|
197
|
+
|
|
198
|
+
| Property | Type | Required | Description |
|
|
199
|
+
|----------|------|:--------:|-------------|
|
|
200
|
+
| `origin` | `string \| string[] \| Function` | ✓ | Path pattern to match |
|
|
201
|
+
| `options` | `string \| ExtendedProxyOptions` | ✓ | Proxy options or target URL |
|
|
202
|
+
|
|
203
|
+
### ExtendedProxyOptions
|
|
204
|
+
|
|
205
|
+
| Property | Type | Required | Description |
|
|
206
|
+
|----------|------|:--------:|-------------|
|
|
207
|
+
| `target` | `string` | ✓ | Target URL |
|
|
208
|
+
| `pathRewrite` | `object` | | Path rewrite rules |
|
|
209
|
+
| `changeOrigin` | `boolean` | | Change origin header (default: false) |
|
|
210
|
+
| `onProxyReq` | `Function` | | Request callback (4th arg: ctx) |
|
|
211
|
+
| `onProxyRes` | `Function` | | Response callback (4th arg: ctx) |
|
|
212
|
+
| `onError` | `Function` | | Error callback |
|
|
213
|
+
| `ws` | `boolean` | | WebSocket proxy (default: false) |
|
|
214
|
+
| `proxyTimeout` | `number` | | Timeout in milliseconds |
|
|
215
|
+
|
|
216
|
+
For more options, see [http-proxy-middleware docs](https://github.com/chimurai/http-proxy-middleware#options).
|
|
217
|
+
|
|
218
|
+
## TypeScript
|
|
219
|
+
|
|
220
|
+
Built-in type definitions:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
// config/config.default.ts
|
|
224
|
+
import { EggAppConfig } from 'egg'
|
|
225
|
+
|
|
226
|
+
export default () => {
|
|
227
|
+
const config: EggAppConfig = {}
|
|
228
|
+
config.httpProxyPlus = {
|
|
229
|
+
'/api': {
|
|
230
|
+
target: 'http://backend.example.com',
|
|
231
|
+
pathRewrite: { '^/api': '' },
|
|
232
|
+
onProxyReq(proxyReq, req, res, ctx) {
|
|
233
|
+
const token = ctx.cookies.get('token')
|
|
234
|
+
token && proxyReq.setHeader('Authorization', token)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return config
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## FAQ
|
|
243
|
+
|
|
244
|
+
<details>
|
|
245
|
+
<summary><b>How to proxy file uploads?</b></summary>
|
|
246
|
+
|
|
247
|
+
Built-in support, no extra configuration needed.
|
|
248
|
+
</details>
|
|
249
|
+
|
|
250
|
+
<details>
|
|
251
|
+
<summary><b>How are POST request bodies handled?</b></summary>
|
|
252
|
+
|
|
253
|
+
The plugin automatically handles `rawBody` for proper forwarding.
|
|
254
|
+
</details>
|
|
255
|
+
|
|
256
|
+
<details>
|
|
257
|
+
<summary><b>How to debug proxy requests?</b></summary>
|
|
258
|
+
|
|
259
|
+
```js
|
|
260
|
+
onProxyReq(proxyReq, req, res, ctx) {
|
|
261
|
+
console.log('[Proxy]', req.method, req.url, '->', proxyReq.path)
|
|
262
|
+
}
|
|
263
|
+
onProxyRes(proxyRes, req, res) {
|
|
264
|
+
console.log('[Response]', proxyRes.statusCode)
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
</details>
|
|
268
|
+
|
|
269
|
+
<details>
|
|
270
|
+
<summary><b>How to skip certain requests?</b></summary>
|
|
271
|
+
|
|
272
|
+
Return `false` in custom matching function:
|
|
273
|
+
```js
|
|
274
|
+
origin(pathname, req) {
|
|
275
|
+
if (pathname === '/health') return false
|
|
276
|
+
return pathname.startsWith('/api')
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
</details>
|
|
280
|
+
|
|
281
|
+
## Contributing
|
|
282
|
+
|
|
283
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
131
284
|
|
|
132
285
|
## License
|
|
133
286
|
|
|
134
287
|
[MIT](LICENSE)
|
|
135
288
|
|
|
289
|
+
---
|
|
136
290
|
|
|
137
291
|
[npm-image]: https://img.shields.io/npm/v/egg-http-proxy-plus.svg?style=flat-square
|
|
138
292
|
[npm-url]: https://npmjs.org/package/egg-http-proxy-plus
|
|
139
|
-
[codacy-image]: https://app.codacy.com/project/badge/Grade/f70d4880e4ad4f40aa970eb9ee9d0696
|
|
140
|
-
[codacy-url]: https://www.codacy.com/gh/saqqdy/egg-http-proxy-plus/dashboard?utm_source=github.com&utm_medium=referral&utm_content=saqqdy/egg-http-proxy-plus&utm_campaign=Badge_Grade
|
|
141
|
-
[travis-image]: https://travis-ci.com/saqqdy/egg-http-proxy-plus.svg?branch=master
|
|
142
|
-
[travis-url]: https://travis-ci.com/saqqdy/egg-http-proxy-plus
|
|
143
|
-
[codecov-image]: https://img.shields.io/codecov/c/github/saqqdy/egg-http-proxy-plus.svg?style=flat-square
|
|
144
|
-
[codecov-url]: https://codecov.io/github/saqqdy/egg-http-proxy-plus?branch=master
|
|
145
293
|
[download-image]: https://img.shields.io/npm/dm/egg-http-proxy-plus.svg?style=flat-square
|
|
146
294
|
[download-url]: https://npmjs.org/package/egg-http-proxy-plus
|
|
147
|
-
[license-image]: https://img.shields.io/badge/License-MIT-
|
|
295
|
+
[license-image]: https://img.shields.io/badge/License-MIT-yellow.svg
|
|
148
296
|
[license-url]: LICENSE
|
|
149
|
-
[
|
|
150
|
-
[sonar-url]: https://sonarcloud.io/dashboard?id=saqqdy_egg-http-proxy-plus
|
|
151
|
-
|
|
297
|
+
[node-image]: https://img.shields.io/node/v/egg-http-proxy-plus.svg?style=flat-square
|
|
@@ -1,56 +1,115 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const pathMatching = require('egg-path-matching')
|
|
1
4
|
const { createProxyMiddleware } = require('http-proxy-middleware')
|
|
5
|
+
const getType = require('js-cool/lib/getType')
|
|
2
6
|
const c2k = require('koa-connect')
|
|
3
|
-
const pathMatching = require('egg-path-matching')
|
|
4
|
-
const { getType } = require('js-cool')
|
|
5
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Normalize proxy options configuration
|
|
10
|
+
* @param {object | Array} options - Proxy configuration
|
|
11
|
+
* @return {Array} Normalized configuration array
|
|
12
|
+
*/
|
|
13
|
+
function normalizeOptions(options) {
|
|
14
|
+
if (getType(options) === 'object') {
|
|
15
|
+
return Object.keys(options).map(context => ({
|
|
16
|
+
origin: context,
|
|
17
|
+
options: options[context],
|
|
18
|
+
}))
|
|
19
|
+
}
|
|
20
|
+
return options
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Build proxy options with event handlers
|
|
25
|
+
* @param {object | string} proxyOptions - Proxy options or target string
|
|
26
|
+
* @param {object} ctx - Koa context
|
|
27
|
+
* @return {object} Processed proxy options
|
|
28
|
+
*/
|
|
29
|
+
function buildProxyOptions(proxyOptions, ctx) {
|
|
30
|
+
if (typeof proxyOptions === 'string') {
|
|
31
|
+
proxyOptions = { target: proxyOptions }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { onProxyReq = null, onProxyRes = null } = proxyOptions
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
...proxyOptions,
|
|
38
|
+
/**
|
|
39
|
+
* Handle proxy request event
|
|
40
|
+
* @param {object} proxyReq - Proxy request object
|
|
41
|
+
* @param {object} req - Original request
|
|
42
|
+
* @param {object} res - Response object
|
|
43
|
+
*/
|
|
44
|
+
onProxyReq(proxyReq, req, res) {
|
|
45
|
+
if (onProxyReq && typeof onProxyReq === 'function') {
|
|
46
|
+
onProxyReq(proxyReq, req, res, ctx)
|
|
47
|
+
}
|
|
48
|
+
// Handle request body for POST/PUT methods
|
|
49
|
+
const { rawBody, body: requestBody } = ctx.request
|
|
50
|
+
if (requestBody && rawBody) {
|
|
51
|
+
proxyReq.setHeader('Content-Length', Buffer.byteLength(rawBody))
|
|
52
|
+
proxyReq.write(rawBody)
|
|
53
|
+
proxyReq.end()
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
/**
|
|
57
|
+
* Handle proxy response event
|
|
58
|
+
* @param {object} proxyRes - Proxy response object
|
|
59
|
+
* @param {object} req - Original request
|
|
60
|
+
* @param {object} res - Response object
|
|
61
|
+
*/
|
|
62
|
+
onProxyRes(proxyRes, req, res) {
|
|
63
|
+
if (onProxyRes && typeof onProxyRes === 'function') {
|
|
64
|
+
onProxyRes(proxyRes, req, res, ctx)
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Check if path matches the context
|
|
72
|
+
* @param {object} context - Context configuration
|
|
73
|
+
* @param {string} path - Request path
|
|
74
|
+
* @param {object} ctx - Koa context
|
|
75
|
+
* @return {boolean} Match result
|
|
76
|
+
*/
|
|
77
|
+
function isPathMatch(context, path, ctx) {
|
|
78
|
+
if (getType(context.origin) === 'function') {
|
|
79
|
+
// Custom matching function
|
|
80
|
+
return context.origin(path.split('?')[0], ctx.req)
|
|
81
|
+
}
|
|
82
|
+
// String or array matching via egg-path-matching
|
|
83
|
+
const match = pathMatching({ match: context.origin })
|
|
84
|
+
return match({ path })
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Egg.js HTTP proxy middleware
|
|
89
|
+
* @param {object | Array} options - Proxy configuration
|
|
90
|
+
* @return {Function} Koa middleware
|
|
91
|
+
*/
|
|
6
92
|
module.exports = options => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (requestBody && rawBody) {
|
|
30
|
-
proxyReq.setHeader('Content-Length', Buffer.byteLength(rawBody))
|
|
31
|
-
proxyReq.write(rawBody)
|
|
32
|
-
proxyReq.end()
|
|
33
|
-
}
|
|
34
|
-
return proxyReq
|
|
35
|
-
},
|
|
36
|
-
// eslint-disable-next-line no-unused-vars
|
|
37
|
-
onProxyRes(proxyRes, req, res, context = ctx) {
|
|
38
|
-
if (onProxyRes && typeof onProxyRes === 'function') {
|
|
39
|
-
onProxyRes(proxyRes, req, res, ctx)
|
|
40
|
-
}
|
|
41
|
-
return proxyRes
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
if (getType(context.origin) === 'function') {
|
|
45
|
-
// custom matching
|
|
46
|
-
isMatch = context.origin(path.split('?')[0], ctx.req)
|
|
47
|
-
} else {
|
|
48
|
-
// context.origin配置的数组、字符串
|
|
49
|
-
const match = pathMatching({ match: context.origin })
|
|
50
|
-
isMatch = match({ path })
|
|
51
|
-
}
|
|
52
|
-
isMatch && (await c2k(createProxyMiddleware(context.origin, proxyOptions))(ctx, next))
|
|
53
|
-
}
|
|
54
|
-
await next()
|
|
55
|
-
}
|
|
93
|
+
// Pre-normalize options for better performance
|
|
94
|
+
const normalizedConfigs = normalizeOptions(options)
|
|
95
|
+
|
|
96
|
+
return async (ctx, next) => {
|
|
97
|
+
const path = ctx.request.originalUrl || ctx.request.url
|
|
98
|
+
|
|
99
|
+
for (const context of normalizedConfigs) {
|
|
100
|
+
if (isPathMatch(context, path, ctx)) {
|
|
101
|
+
const proxyOptions = buildProxyOptions(context.options, ctx)
|
|
102
|
+
// http-proxy-middleware v3: pass context as part of options
|
|
103
|
+
const middleware = createProxyMiddleware({
|
|
104
|
+
...proxyOptions,
|
|
105
|
+
context: context.origin,
|
|
106
|
+
})
|
|
107
|
+
await c2k(middleware)(ctx, next)
|
|
108
|
+
// Return early to prevent double next() call
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
await next()
|
|
114
|
+
}
|
|
56
115
|
}
|
package/app.js
CHANGED
package/config/config.default.js
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* egg-http-proxy-plus default config
|
|
3
3
|
* @member Config#httpProxyPlus
|
|
4
|
-
* @property {
|
|
4
|
+
* @property {object | Array} httpProxyPlus - Proxy configuration
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* // Object style configuration
|
|
8
|
+
* exports.httpProxyPlus = {
|
|
9
|
+
* '/api': 'http://target.com',
|
|
10
|
+
* '/v1': { target: 'http://target.com', pathRewrite: { '^/v1': '' } }
|
|
11
|
+
* }
|
|
12
|
+
*
|
|
13
|
+
* // Array style configuration (for more control)
|
|
14
|
+
* exports.httpProxyPlus = [
|
|
15
|
+
* { origin: '/api', options: 'http://target.com' },
|
|
16
|
+
* {
|
|
17
|
+
* origin: pathname => pathname.startsWith('/api'),
|
|
18
|
+
* options: { target: 'http://target.com' }
|
|
19
|
+
* }
|
|
20
|
+
* ]
|
|
5
21
|
*/
|
|
22
|
+
'use strict'
|
|
23
|
+
|
|
6
24
|
exports.httpProxyPlus = {}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Context, Application } from 'egg'
|
|
2
|
+
import { Options } from 'http-proxy-middleware'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Context extension for egg-http-proxy-plus
|
|
6
|
+
*/
|
|
7
|
+
declare module 'egg' {
|
|
8
|
+
interface Context {
|
|
9
|
+
request: {
|
|
10
|
+
rawBody?: string
|
|
11
|
+
body?: unknown
|
|
12
|
+
originalUrl: string
|
|
13
|
+
url: string
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Proxy request callback function
|
|
20
|
+
*/
|
|
21
|
+
type ProxyReqCallback = (
|
|
22
|
+
proxyReq: unknown,
|
|
23
|
+
req: unknown,
|
|
24
|
+
res: unknown,
|
|
25
|
+
ctx: Context
|
|
26
|
+
) => void
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Proxy response callback function
|
|
30
|
+
*/
|
|
31
|
+
type ProxyResCallback = (
|
|
32
|
+
proxyRes: unknown,
|
|
33
|
+
req: unknown,
|
|
34
|
+
res: unknown,
|
|
35
|
+
ctx: Context
|
|
36
|
+
) => void
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Extended proxy options with ctx context
|
|
40
|
+
*/
|
|
41
|
+
interface ExtendedProxyOptions extends Omit<Options, 'onProxyReq' | 'onProxyRes'> {
|
|
42
|
+
target: string
|
|
43
|
+
onProxyReq?: ProxyReqCallback
|
|
44
|
+
onProxyRes?: ProxyResCallback
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Custom matching function type
|
|
49
|
+
*/
|
|
50
|
+
type CustomMatcher = (pathname: string, req: unknown) => boolean
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Single proxy configuration item
|
|
54
|
+
*/
|
|
55
|
+
interface ProxyConfigItem {
|
|
56
|
+
/**
|
|
57
|
+
* Path pattern to match
|
|
58
|
+
* - string: single path pattern
|
|
59
|
+
* - string[]: multiple path patterns
|
|
60
|
+
* - function: custom matching function
|
|
61
|
+
*/
|
|
62
|
+
origin: string | string[] | CustomMatcher
|
|
63
|
+
/**
|
|
64
|
+
* Proxy options, can be a target URL string or options object
|
|
65
|
+
*/
|
|
66
|
+
options: string | ExtendedProxyOptions
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Object-based proxy configuration
|
|
71
|
+
* Key is the path pattern, value is the target or options
|
|
72
|
+
*/
|
|
73
|
+
type ProxyConfigObject = Record<string, string | ExtendedProxyOptions>
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Main plugin configuration
|
|
77
|
+
* Can be an object mapping paths to targets, or an array of detailed configs
|
|
78
|
+
*/
|
|
79
|
+
type HttpProxyPlusConfig = ProxyConfigObject | ProxyConfigItem[]
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Plugin configuration in config
|
|
83
|
+
*/
|
|
84
|
+
declare module '../config/config.default' {
|
|
85
|
+
interface EggAppConfig {
|
|
86
|
+
httpProxyPlus?: HttpProxyPlusConfig
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Plugin declaration
|
|
92
|
+
*/
|
|
93
|
+
declare module 'egg' {
|
|
94
|
+
interface EggPlugin {
|
|
95
|
+
httpProxyPlus?: boolean
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export default HttpProxyPlusConfig
|
|
100
|
+
export type {
|
|
101
|
+
ProxyConfigItem,
|
|
102
|
+
ProxyConfigObject,
|
|
103
|
+
ExtendedProxyOptions,
|
|
104
|
+
ProxyReqCallback,
|
|
105
|
+
ProxyResCallback,
|
|
106
|
+
CustomMatcher,
|
|
107
|
+
HttpProxyPlusConfig
|
|
108
|
+
}
|
package/package.json
CHANGED
|
@@ -1,107 +1,70 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
"@types/node",
|
|
71
|
-
"webpack",
|
|
72
|
-
"typescript",
|
|
73
|
-
"mocha"
|
|
74
|
-
],
|
|
75
|
-
"allowedVersions": {
|
|
76
|
-
"eslint": ">= 8.0.0",
|
|
77
|
-
"fsevents": ">= 2.0.0"
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
"keywords": [
|
|
82
|
-
"egg",
|
|
83
|
-
"eggPlugin",
|
|
84
|
-
"egg-plugin",
|
|
85
|
-
"proxy",
|
|
86
|
-
"http-proxy",
|
|
87
|
-
"http-proxy-plus",
|
|
88
|
-
"http-proxy-middleware"
|
|
89
|
-
],
|
|
90
|
-
"publishConfig": {
|
|
91
|
-
"registry": "https://registry.npmjs.org",
|
|
92
|
-
"access": "public"
|
|
93
|
-
},
|
|
94
|
-
"ci": {
|
|
95
|
-
"version": "12, 14, 16, 18"
|
|
96
|
-
},
|
|
97
|
-
"license": "MIT",
|
|
98
|
-
"author": "saqqdy <https://github.com/saqqdy>",
|
|
99
|
-
"homepage": "https://github.com/saqqdy/egg-http-proxy-plus#readme",
|
|
100
|
-
"bugs": {
|
|
101
|
-
"url": "https://github.com/saqqdy/egg-http-proxy-plus/issues"
|
|
102
|
-
},
|
|
103
|
-
"repository": {
|
|
104
|
-
"type": "git",
|
|
105
|
-
"url": "git+https://github.com/saqqdy/egg-http-proxy-plus.git"
|
|
106
|
-
}
|
|
2
|
+
"name": "egg-http-proxy-plus",
|
|
3
|
+
"description": "A powerful HTTP proxy middleware plugin for Egg.js with TypeScript support",
|
|
4
|
+
"version": "2.1.0",
|
|
5
|
+
"main": "app.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"app",
|
|
9
|
+
"config",
|
|
10
|
+
"app.js",
|
|
11
|
+
"index.d.ts"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"lint": "eslint .",
|
|
15
|
+
"lint:fix": "eslint . --fix",
|
|
16
|
+
"test": "npm run lint && egg-bin test",
|
|
17
|
+
"test:local": "egg-bin test",
|
|
18
|
+
"cov": "egg-bin cov",
|
|
19
|
+
"ci": "npm run lint && npm run cov"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"egg-path-matching": "^1.2.0",
|
|
23
|
+
"http-proxy-middleware": "^3.0.5",
|
|
24
|
+
"js-cool": "^2.1.2",
|
|
25
|
+
"koa-connect": "^2.1.1"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@eslint-sets/eslint-config": "^6.3.1",
|
|
29
|
+
"egg": "^3.34.0",
|
|
30
|
+
"egg-bin": "^6.13.0",
|
|
31
|
+
"egg-ci": "^2.2.0",
|
|
32
|
+
"egg-mock": "^5.15.2",
|
|
33
|
+
"eslint": "^9.39.4",
|
|
34
|
+
"express": "^4.22.1"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"egg": ">=3.0.0"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=14.0.0"
|
|
41
|
+
},
|
|
42
|
+
"eggPlugin": {
|
|
43
|
+
"name": "httpProxyPlus"
|
|
44
|
+
},
|
|
45
|
+
"ci": {
|
|
46
|
+
"version": "18, 20, 22"
|
|
47
|
+
},
|
|
48
|
+
"sideEffects": false,
|
|
49
|
+
"keywords": [
|
|
50
|
+
"egg",
|
|
51
|
+
"egg-plugin",
|
|
52
|
+
"eggPlugin",
|
|
53
|
+
"proxy",
|
|
54
|
+
"http-proxy",
|
|
55
|
+
"http-proxy-middleware",
|
|
56
|
+
"middleware",
|
|
57
|
+
"websocket",
|
|
58
|
+
"typescript"
|
|
59
|
+
],
|
|
60
|
+
"license": "MIT",
|
|
61
|
+
"author": "saqqdy <saqqdy@qq.com>",
|
|
62
|
+
"homepage": "https://github.com/saqqdy/egg-http-proxy-plus#readme",
|
|
63
|
+
"bugs": {
|
|
64
|
+
"url": "https://github.com/saqqdy/egg-http-proxy-plus/issues"
|
|
65
|
+
},
|
|
66
|
+
"repository": {
|
|
67
|
+
"type": "git",
|
|
68
|
+
"url": "git+https://github.com/saqqdy/egg-http-proxy-plus.git"
|
|
69
|
+
}
|
|
107
70
|
}
|