@gylautorun/dev-proxy-cookie 1.0.1 → 1.0.3
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 +212 -236
- package/dist/index.d.mts +304 -67
- package/dist/index.d.ts +304 -67
- package/dist/index.js +314 -173
- package/dist/index.min.js +4 -4
- package/dist/index.min.mjs +4 -4
- package/dist/index.mjs +313 -168
- package/package.json +1 -1
- package/src/index.ts +13 -0
- package/src/proxy/apply-dev-cookie-header.ts +35 -5
- package/src/proxy/core.ts +198 -12
- package/src/proxy/index.ts +8 -3
- package/src/proxy/vite-middleware-plugin.ts +177 -0
- package/src/proxy/vue-proxy-config.ts +98 -18
- package/src/utils/cookie-reader.ts +74 -3
- package/src/utils/cookie-watcher.ts +50 -1
- package/src/utils/env-detector.ts +9 -2
- package/src/utils/index.ts +7 -0
- package/src/proxy/vite-adapter.ts +0 -98
- package/src/proxy/vite-cookie-plugin.ts +0 -94
- package/src/proxy/vite-plugin.ts +0 -66
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,2 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dev-proxy-cookie 模块入口文件
|
|
3
|
+
*
|
|
4
|
+
* 提供开发环境下的 Cookie 代理和文件监听功能,支持 Vue CLI 和 Vite 构建工具。
|
|
5
|
+
*
|
|
6
|
+
* 主要功能:
|
|
7
|
+
* - Cookie 文件读取与监听
|
|
8
|
+
* - 代理请求时自动注入 Cookie
|
|
9
|
+
* - 支持 Vue CLI 和 Vite 两种构建工具
|
|
10
|
+
* - 智能环境检测,自动启用/禁用文件监听
|
|
11
|
+
*
|
|
12
|
+
* @module dev-proxy-cookie
|
|
13
|
+
*/
|
|
1
14
|
export * from './proxy';
|
|
2
15
|
export * from './utils';
|
|
@@ -1,14 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* 应用开发环境 Cookie 到代理请求头
|
|
3
|
+
*
|
|
4
|
+
* 将文件中读取的 Cookie 设置到代理请求中,替换浏览器发送的 Cookie。
|
|
5
|
+
* 这是必要的,因为当 withCredentials 为 true 时,浏览器会发送其自身的 Cookie,
|
|
6
|
+
* 与文件中的 Cookie 合并可能导致重复的 Cookie 名称(如两个 JSESSIONID),
|
|
7
|
+
* 而服务器通常会优先使用第一个值(浏览器的旧会话)。
|
|
8
|
+
*
|
|
9
|
+
* @param proxyReq - 代理请求对象
|
|
10
|
+
* @param cookie - 要设置的 Cookie 字符串
|
|
5
11
|
*/
|
|
6
12
|
export function applyDevCookieHeader(
|
|
7
|
-
proxyReq: { removeHeader(name: string): void; setHeader(name: string, value: string): void },
|
|
13
|
+
proxyReq: { removeHeader(name: string): void; setHeader(name: string, value: string): void; getHeader?(name: string): string | undefined },
|
|
8
14
|
cookie: string
|
|
9
15
|
): void {
|
|
10
|
-
|
|
16
|
+
console.log('[applyDevCookieHeader] === START ===');
|
|
17
|
+
console.log('[applyDevCookieHeader] Cookie to apply:', cookie ? `(length: ${cookie.length})` : '(empty)');
|
|
18
|
+
|
|
19
|
+
if (!cookie) {
|
|
20
|
+
console.log('[applyDevCookieHeader] Cookie is empty, returning');
|
|
21
|
+
console.log('[applyDevCookieHeader] === END ===');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// 尝试获取当前的 cookie 头(如果有)
|
|
26
|
+
const existingCookie = proxyReq.getHeader?.('Cookie');
|
|
27
|
+
console.log('[applyDevCookieHeader] Cookie current:', existingCookie ? `(length: ${String(existingCookie).length})` : '(none)');
|
|
28
|
+
|
|
29
|
+
// 如果现有 Cookie 与要设置的 Cookie 相同,直接跳过处理
|
|
30
|
+
if (existingCookie === cookie) {
|
|
31
|
+
console.log('[applyDevCookieHeader] Cookie is already set, skipping');
|
|
32
|
+
console.log('[applyDevCookieHeader] === END ===');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
11
36
|
proxyReq.removeHeader('cookie');
|
|
12
37
|
proxyReq.removeHeader('Cookie');
|
|
13
38
|
proxyReq.setHeader('Cookie', cookie);
|
|
39
|
+
|
|
40
|
+
// 验证是否设置成功
|
|
41
|
+
const newCookie = proxyReq.getHeader?.('Cookie');
|
|
42
|
+
console.log('[applyDevCookieHeader] Cookie new:', newCookie ? `(length: ${String(newCookie).length})` : '(failed)');
|
|
43
|
+
console.log('[applyDevCookieHeader] === END ===');
|
|
14
44
|
}
|
package/src/proxy/core.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自动代理 Cookie 核心模块
|
|
3
|
+
*
|
|
4
|
+
* 提供完整的开发环境代理解决方案,支持 HTTP 和 WebSocket 代理,
|
|
5
|
+
* 自动从文件读取 Cookie 并注入到代理请求中。
|
|
6
|
+
*
|
|
7
|
+
* @module core
|
|
8
|
+
*/
|
|
1
9
|
import * as http from 'http';
|
|
2
10
|
import * as net from 'net';
|
|
3
11
|
import * as fs from 'fs';
|
|
@@ -8,6 +16,12 @@ import httpProxy from 'http-proxy';
|
|
|
8
16
|
import { CookieReader, CookieWatcher, watchCookieFile } from '../utils';
|
|
9
17
|
import { applyDevCookieHeader } from './apply-dev-cookie-header';
|
|
10
18
|
|
|
19
|
+
/**
|
|
20
|
+
* 错误回调函数类型
|
|
21
|
+
* @param err - 错误对象
|
|
22
|
+
* @param req - 请求对象
|
|
23
|
+
* @param res - 响应对象或 Socket
|
|
24
|
+
*/
|
|
11
25
|
export type ErrorCallback = (err: Error, req: IncomingMessage, res: ServerResponse | net.Socket) => void;
|
|
12
26
|
|
|
13
27
|
interface ProxyServer {
|
|
@@ -48,6 +62,12 @@ export interface AutoProxyCookieOptions {
|
|
|
48
62
|
autoRestart?: boolean;
|
|
49
63
|
restartMarkerFile?: string;
|
|
50
64
|
proxyMap?: Record<string, string>;
|
|
65
|
+
/**
|
|
66
|
+
* 需要代理的路径前缀列表(默认代理到 target)
|
|
67
|
+
* 例如: ['/api', '/digital-platform']
|
|
68
|
+
* 如果请求路径匹配这些前缀之一,将被代理到 target
|
|
69
|
+
*/
|
|
70
|
+
proxyPaths?: string[];
|
|
51
71
|
ignorePaths?: string[];
|
|
52
72
|
ws?: boolean;
|
|
53
73
|
changeOrigin?: boolean;
|
|
@@ -60,25 +80,58 @@ export interface AutoProxyCookieOptions {
|
|
|
60
80
|
cookiePathRewrite?: false | string | { [oldPath: string]: string };
|
|
61
81
|
headers?: { [header: string]: string };
|
|
62
82
|
hooks?: ProxyHooks;
|
|
83
|
+
/**
|
|
84
|
+
* 是否启用文件监听
|
|
85
|
+
* - 'auto': 自动(如果 isDev 为 true,则启用监听)
|
|
86
|
+
* - true: 强制启用监听
|
|
87
|
+
* - false: 禁用监听
|
|
88
|
+
*
|
|
89
|
+
* 默认值: 'auto'
|
|
90
|
+
*/
|
|
91
|
+
watch?: 'auto' | boolean;
|
|
63
92
|
/**
|
|
64
93
|
* 是否为开发环境(优先级最高)
|
|
65
94
|
* 设置此参数后,将直接决定是否启用文件监听:
|
|
66
95
|
* - true: 启用监听(开发模式)
|
|
67
96
|
* - false: 禁用监听(生产模式)
|
|
68
|
-
*
|
|
97
|
+
*
|
|
69
98
|
* 使用示例: isDev: process.env.NODE_ENV === 'development'
|
|
70
99
|
*/
|
|
71
100
|
isDev?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* 是否使用 Cookie 文件中的 Cookie 注入到请求中
|
|
103
|
+
* - true: 使用文件中的 Cookie(默认)
|
|
104
|
+
* - false: 不注入 Cookie,使用浏览器发送的 Cookie
|
|
105
|
+
*
|
|
106
|
+
* 当使用账号密码登录时,设置为 false,避免覆盖浏览器的登录 Cookie
|
|
107
|
+
*/
|
|
108
|
+
useCookie?: boolean;
|
|
72
109
|
}
|
|
73
110
|
|
|
111
|
+
/**
|
|
112
|
+
* 自动代理 Cookie 类
|
|
113
|
+
*
|
|
114
|
+
* 提供完整的开发环境代理解决方案,支持 HTTP 和 WebSocket 代理,
|
|
115
|
+
* 自动从文件读取 Cookie 并注入到代理请求中。
|
|
116
|
+
*/
|
|
74
117
|
export class AutoProxyCookie {
|
|
118
|
+
/** 合并后的配置选项 */
|
|
75
119
|
private options: AutoProxyCookieOptions & { hooks: Required<ProxyHooks> };
|
|
120
|
+
/** 当前 Cookie 值 */
|
|
76
121
|
private currentCookie: string = '';
|
|
122
|
+
/** Vite 开发服务器实例 */
|
|
77
123
|
private server: ViteDevServer | null = null;
|
|
124
|
+
/** HTTP 代理服务器实例 */
|
|
78
125
|
private proxyServer: ProxyServer | null = null;
|
|
126
|
+
/** Cookie 文件监听器 */
|
|
79
127
|
private watcher: CookieWatcher | null = null;
|
|
128
|
+
/** Cookie 文件读取器 */
|
|
80
129
|
private cookieReader: CookieReader;
|
|
81
130
|
|
|
131
|
+
/**
|
|
132
|
+
* 构造函数
|
|
133
|
+
* @param options - 配置选项
|
|
134
|
+
*/
|
|
82
135
|
constructor(options: AutoProxyCookieOptions) {
|
|
83
136
|
const defaultHooks: Required<ProxyHooks> = {
|
|
84
137
|
onProxyReq: () => {},
|
|
@@ -100,6 +153,7 @@ export class AutoProxyCookie {
|
|
|
100
153
|
autoRestart: false,
|
|
101
154
|
restartMarkerFile: '.cookie-restart-marker',
|
|
102
155
|
proxyMap: {},
|
|
156
|
+
proxyPaths: [],
|
|
103
157
|
ignorePaths: [],
|
|
104
158
|
ws: true,
|
|
105
159
|
changeOrigin: true,
|
|
@@ -111,11 +165,16 @@ export class AutoProxyCookie {
|
|
|
111
165
|
cookieDomainRewrite: '*',
|
|
112
166
|
cookiePathRewrite: false,
|
|
113
167
|
headers: {},
|
|
168
|
+
useCookie: true,
|
|
114
169
|
...mergedOptions,
|
|
115
170
|
};
|
|
116
|
-
this.cookieReader = new CookieReader({ cookieFile: options.cookieFile });
|
|
171
|
+
this.cookieReader = new CookieReader({ cookieFile: options.cookieFile }, options.debug ?? false);
|
|
117
172
|
}
|
|
118
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Cookie 变化处理函数
|
|
176
|
+
* @param newCookie - 新的 Cookie 值
|
|
177
|
+
*/
|
|
119
178
|
private handleCookieChange = (newCookie: string): void => {
|
|
120
179
|
if (newCookie !== this.currentCookie) {
|
|
121
180
|
this.currentCookie = newCookie;
|
|
@@ -133,19 +192,50 @@ export class AutoProxyCookie {
|
|
|
133
192
|
}
|
|
134
193
|
};
|
|
135
194
|
|
|
195
|
+
/**
|
|
196
|
+
* 根据请求路径获取代理目标 URL
|
|
197
|
+
* @param req - HTTP 请求对象
|
|
198
|
+
* @returns 代理目标 URL
|
|
199
|
+
*/
|
|
136
200
|
private getProxyUrl(req: IncomingMessage): string {
|
|
137
|
-
const
|
|
201
|
+
const fullUrl = req.url || '/';
|
|
202
|
+
const pathname = new URL(fullUrl, 'http://localhost').pathname;
|
|
138
203
|
const proxyMap = this.options.proxyMap || {};
|
|
204
|
+
const proxyPaths = this.options.proxyPaths || [];
|
|
205
|
+
|
|
206
|
+
this.log('debug', '[AutoProxyCookie] getProxyUrl - Request path:', pathname);
|
|
207
|
+
this.log('debug', '[AutoProxyCookie] getProxyUrl - Available proxyMap paths:', Object.keys(proxyMap));
|
|
208
|
+
this.log('debug', '[AutoProxyCookie] getProxyUrl - Available proxyPaths:', proxyPaths);
|
|
139
209
|
|
|
210
|
+
// 首先检查 proxyMap(自定义代理目标)
|
|
140
211
|
for (const [prefix, target] of Object.entries(proxyMap)) {
|
|
141
|
-
|
|
212
|
+
const matches = pathname.startsWith(prefix);
|
|
213
|
+
this.log('debug', '[AutoProxyCookie] getProxyUrl - Checking proxyMap:', prefix, '- matches:', matches);
|
|
214
|
+
if (matches) {
|
|
215
|
+
this.log('debug', '[AutoProxyCookie] getProxyUrl - Matched proxyMap:', prefix, '->', target);
|
|
142
216
|
return target;
|
|
143
217
|
}
|
|
144
218
|
}
|
|
145
219
|
|
|
220
|
+
// 然后检查 proxyPaths(默认代理到 target)
|
|
221
|
+
for (const prefix of proxyPaths) {
|
|
222
|
+
const matches = pathname.startsWith(prefix);
|
|
223
|
+
this.log('debug', '[AutoProxyCookie] getProxyUrl - Checking proxyPaths:', prefix, '- matches:', matches);
|
|
224
|
+
if (matches) {
|
|
225
|
+
this.log('debug', '[AutoProxyCookie] getProxyUrl - Matched proxyPaths:', prefix, '->', this.options.target);
|
|
226
|
+
return this.options.target;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
this.log('debug', '[AutoProxyCookie] getProxyUrl - No match found, using default target:', this.options.target);
|
|
146
231
|
return this.options.target;
|
|
147
232
|
}
|
|
148
233
|
|
|
234
|
+
/**
|
|
235
|
+
* 判断路径是否应该被忽略(不代理)
|
|
236
|
+
* @param pathname - 请求路径
|
|
237
|
+
* @returns 是否忽略
|
|
238
|
+
*/
|
|
149
239
|
private isIgnoredPath(pathname: string): boolean {
|
|
150
240
|
const ignorePaths = this.options.ignorePaths || [];
|
|
151
241
|
return ignorePaths.some(ignored =>
|
|
@@ -153,9 +243,16 @@ export class AutoProxyCookie {
|
|
|
153
243
|
);
|
|
154
244
|
}
|
|
155
245
|
|
|
246
|
+
/**
|
|
247
|
+
* 日志输出函数
|
|
248
|
+
* @param level - 日志级别
|
|
249
|
+
* @param args - 日志参数
|
|
250
|
+
*/
|
|
156
251
|
private log(level: 'debug' | 'info' | 'warn' | 'error', ...args: any[]): void {
|
|
157
252
|
const levels: Record<string, number> = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
158
|
-
|
|
253
|
+
// 如果设置了 debug: true,则使用 debug 级别
|
|
254
|
+
const effectiveLogLevel = this.options.debug ? 'debug' : (this.options.logLevel || 'info');
|
|
255
|
+
const currentLevel = levels[effectiveLogLevel];
|
|
159
256
|
const msgLevel = levels[level];
|
|
160
257
|
|
|
161
258
|
if (msgLevel >= currentLevel) {
|
|
@@ -169,6 +266,11 @@ export class AutoProxyCookie {
|
|
|
169
266
|
}
|
|
170
267
|
}
|
|
171
268
|
|
|
269
|
+
/**
|
|
270
|
+
* 创建代理服务器配置选项
|
|
271
|
+
* @param target - 代理目标地址
|
|
272
|
+
* @returns 代理服务器配置
|
|
273
|
+
*/
|
|
172
274
|
private createProxyOptions(target: string): ServerOptions {
|
|
173
275
|
const {
|
|
174
276
|
ws,
|
|
@@ -199,9 +301,27 @@ export class AutoProxyCookie {
|
|
|
199
301
|
};
|
|
200
302
|
}
|
|
201
303
|
|
|
304
|
+
/**
|
|
305
|
+
* 代理请求处理函数
|
|
306
|
+
* @param proxyReq - 代理请求对象
|
|
307
|
+
* @param req - 原始请求对象
|
|
308
|
+
* @param res - 响应对象
|
|
309
|
+
* @param _options - 服务器选项
|
|
310
|
+
*/
|
|
202
311
|
private handleOnProxyReq = (proxyReq: any, req: IncomingMessage, res: ServerResponse, _options: ServerOptions): void => {
|
|
203
|
-
|
|
312
|
+
console.log('[AutoProxyCookie] === handleOnProxyReq START ===');
|
|
313
|
+
console.log('[AutoProxyCookie] Request URL:', req.method, req.url);
|
|
314
|
+
console.log('[AutoProxyCookie] useCookie:', this.options.useCookie);
|
|
315
|
+
console.log('[AutoProxyCookie] Current cookie:', this.currentCookie ? `(length: ${this.currentCookie.length})` : '(empty)');
|
|
316
|
+
|
|
317
|
+
if (this.options.useCookie && this.currentCookie) {
|
|
318
|
+
console.log('[AutoProxyCookie] Applying cookie header...');
|
|
204
319
|
applyDevCookieHeader(proxyReq, this.currentCookie);
|
|
320
|
+
console.log('[AutoProxyCookie] Cookie header applied successfully');
|
|
321
|
+
} else if (!this.options.useCookie) {
|
|
322
|
+
console.log('[AutoProxyCookie] useCookie is false, skipping cookie injection');
|
|
323
|
+
} else {
|
|
324
|
+
console.log('[AutoProxyCookie] No cookie to apply - currentCookie is empty!');
|
|
205
325
|
}
|
|
206
326
|
|
|
207
327
|
this.log('debug', '[AutoProxyCookie] Proxy Request:', req.method, req.url);
|
|
@@ -213,8 +333,16 @@ export class AutoProxyCookie {
|
|
|
213
333
|
this.log('error', '[AutoProxyCookie] onProxyReq hook error:', (err as Error).message);
|
|
214
334
|
}
|
|
215
335
|
}
|
|
336
|
+
|
|
337
|
+
console.log('[AutoProxyCookie] === handleOnProxyReq END ===');
|
|
216
338
|
};
|
|
217
339
|
|
|
340
|
+
/**
|
|
341
|
+
* 代理响应处理函数
|
|
342
|
+
* @param proxyRes - 代理响应对象
|
|
343
|
+
* @param req - 原始请求对象
|
|
344
|
+
* @param res - 响应对象
|
|
345
|
+
*/
|
|
218
346
|
private handleOnProxyRes = (proxyRes: any, req: IncomingMessage, res: ServerResponse): void => {
|
|
219
347
|
const allowedHeaders = [
|
|
220
348
|
'Content-Type',
|
|
@@ -242,6 +370,12 @@ export class AutoProxyCookie {
|
|
|
242
370
|
}
|
|
243
371
|
};
|
|
244
372
|
|
|
373
|
+
/**
|
|
374
|
+
* HTTP 代理错误处理函数
|
|
375
|
+
* @param err - 错误对象
|
|
376
|
+
* @param req - 请求对象
|
|
377
|
+
* @param res - 响应对象或 Socket
|
|
378
|
+
*/
|
|
245
379
|
private handleOnError = (err: Error, req: IncomingMessage, res: ServerResponse | net.Socket): void => {
|
|
246
380
|
this.log('error', '[AutoProxyCookie] Proxy Error:', err.message);
|
|
247
381
|
this.log('error', '[AutoProxyCookie] URL:', req.url);
|
|
@@ -264,6 +398,12 @@ export class AutoProxyCookie {
|
|
|
264
398
|
}
|
|
265
399
|
};
|
|
266
400
|
|
|
401
|
+
/**
|
|
402
|
+
* WebSocket 代理错误处理函数
|
|
403
|
+
* @param err - 错误对象
|
|
404
|
+
* @param req - 请求对象
|
|
405
|
+
* @param socket - WebSocket 连接的 Socket
|
|
406
|
+
*/
|
|
267
407
|
private handleOnWsError = (err: Error, req: IncomingMessage, socket: any): void => {
|
|
268
408
|
this.log('error', '[AutoProxyCookie] WebSocket Proxy Error:', err.message);
|
|
269
409
|
this.log('error', '[AutoProxyCookie] WebSocket URL:', req.url);
|
|
@@ -281,6 +421,10 @@ export class AutoProxyCookie {
|
|
|
281
421
|
}
|
|
282
422
|
};
|
|
283
423
|
|
|
424
|
+
/**
|
|
425
|
+
* 初始化代理中间件
|
|
426
|
+
* @param server - Vite 开发服务器实例
|
|
427
|
+
*/
|
|
284
428
|
async setup(server: ViteDevServer): Promise<void> {
|
|
285
429
|
this.server = server;
|
|
286
430
|
this.currentCookie = this.cookieReader.readCookie();
|
|
@@ -313,30 +457,57 @@ export class AutoProxyCookie {
|
|
|
313
457
|
}
|
|
314
458
|
|
|
315
459
|
server.middlewares.use((req: any, res: any, next: any) => {
|
|
316
|
-
const
|
|
460
|
+
const fullUrl = req.url || '/';
|
|
461
|
+
const pathname = new URL(fullUrl, 'http://localhost').pathname;
|
|
462
|
+
|
|
463
|
+
// 始终输出请求信息(不仅仅是 debug 模式)
|
|
464
|
+
console.log('[AutoProxyCookie] === Incoming Request ===');
|
|
465
|
+
console.log('[AutoProxyCookie] Method:', req.method);
|
|
466
|
+
console.log('[AutoProxyCookie] Full URL:', fullUrl);
|
|
467
|
+
console.log('[AutoProxyCookie] Pathname:', pathname);
|
|
468
|
+
console.log('[AutoProxyCookie] useCookie:', this.options.useCookie);
|
|
469
|
+
console.log('[AutoProxyCookie] Headers:', JSON.stringify(req.headers, null, 2));
|
|
317
470
|
|
|
318
471
|
if (this.isIgnoredPath(pathname)) {
|
|
472
|
+
console.log('[AutoProxyCookie] Path ignored, passing to next middleware');
|
|
319
473
|
next();
|
|
320
474
|
return;
|
|
321
475
|
}
|
|
322
476
|
|
|
323
|
-
|
|
477
|
+
// 如果启用 Cookie,读取 cookie 并更新 currentCookie
|
|
478
|
+
if (this.options.useCookie) {
|
|
479
|
+
this.currentCookie = this.cookieReader.readCookie();
|
|
480
|
+
console.log('[AutoProxyCookie] Current cookie:', this.currentCookie ? `(length: ${this.currentCookie.length})` : '(empty)');
|
|
481
|
+
if (this.currentCookie) {
|
|
482
|
+
console.log('[AutoProxyCookie] Cookie preview:', this.currentCookie);
|
|
483
|
+
}
|
|
484
|
+
} else {
|
|
485
|
+
console.log('[AutoProxyCookie] useCookie is false, skipping cookie reading');
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// 检查是否匹配代理路径(包括 proxyMap 和 proxyPaths)
|
|
489
|
+
const proxyMapKeys = Object.keys(this.options.proxyMap || {});
|
|
490
|
+
const proxyPaths = this.options.proxyPaths || [];
|
|
491
|
+
const allProxyPrefixes = [...proxyMapKeys, ...proxyPaths];
|
|
492
|
+
const matched = allProxyPrefixes.some(prefix => pathname.startsWith(prefix));
|
|
493
|
+
console.log('[AutoProxyCookie] Path matches proxy rules:', matched);
|
|
324
494
|
|
|
325
495
|
if (this.proxyServer) {
|
|
326
496
|
const target = this.getProxyUrl(req);
|
|
327
|
-
|
|
328
|
-
this.log('info', `[AutoProxyCookie] ${pathname} -> ${target}`);
|
|
329
|
-
}
|
|
497
|
+
console.log(`[AutoProxyCookie] Proxying ${req.method} ${pathname} -> ${target}`);
|
|
330
498
|
|
|
331
499
|
const proxyOptions = this.createProxyOptions(target);
|
|
332
500
|
|
|
333
501
|
try {
|
|
502
|
+
console.log('[AutoProxyCookie] Calling proxyServer.web...');
|
|
334
503
|
this.proxyServer.web(req, res, proxyOptions);
|
|
504
|
+
console.log('[AutoProxyCookie] proxyServer.web called successfully');
|
|
335
505
|
} catch (err: any) {
|
|
336
|
-
|
|
506
|
+
console.error('[AutoProxyCookie] Proxy web error:', err.message);
|
|
337
507
|
next(err);
|
|
338
508
|
}
|
|
339
509
|
} else {
|
|
510
|
+
console.log('[AutoProxyCookie] No proxy server, passing to next middleware');
|
|
340
511
|
next();
|
|
341
512
|
}
|
|
342
513
|
});
|
|
@@ -376,6 +547,9 @@ export class AutoProxyCookie {
|
|
|
376
547
|
}
|
|
377
548
|
}
|
|
378
549
|
|
|
550
|
+
/**
|
|
551
|
+
* 启动 Cookie 文件监听
|
|
552
|
+
*/
|
|
379
553
|
private startFileWatch(): void {
|
|
380
554
|
// 判断是否应该启用监听
|
|
381
555
|
// isDev 参数优先级最高
|
|
@@ -407,6 +581,9 @@ export class AutoProxyCookie {
|
|
|
407
581
|
}
|
|
408
582
|
}
|
|
409
583
|
|
|
584
|
+
/**
|
|
585
|
+
* 停止代理服务
|
|
586
|
+
*/
|
|
410
587
|
stop(): void {
|
|
411
588
|
if (this.watcher) {
|
|
412
589
|
this.watcher.stop();
|
|
@@ -419,11 +596,20 @@ export class AutoProxyCookie {
|
|
|
419
596
|
this.log('info', '[AutoProxyCookie] Stopped');
|
|
420
597
|
}
|
|
421
598
|
|
|
599
|
+
/**
|
|
600
|
+
* 获取当前 Cookie 值
|
|
601
|
+
* @returns 当前 Cookie 字符串
|
|
602
|
+
*/
|
|
422
603
|
getCurrentCookie(): string {
|
|
423
604
|
return this.currentCookie;
|
|
424
605
|
}
|
|
425
606
|
}
|
|
426
607
|
|
|
608
|
+
/**
|
|
609
|
+
* 创建 AutoProxyCookie 实例
|
|
610
|
+
* @param options - 配置选项
|
|
611
|
+
* @returns AutoProxyCookie 实例
|
|
612
|
+
*/
|
|
427
613
|
export function createAutoProxyCookie(options: AutoProxyCookieOptions): AutoProxyCookie {
|
|
428
614
|
return new AutoProxyCookie(options);
|
|
429
615
|
}
|
package/src/proxy/index.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 代理模块导出文件
|
|
3
|
+
*
|
|
4
|
+
* 包含 Vue CLI 和 Vite 的代理配置实现,提供完整的开发环境代理解决方案。
|
|
5
|
+
*
|
|
6
|
+
* @module proxy
|
|
7
|
+
*/
|
|
1
8
|
export * from './core';
|
|
2
|
-
export * from './vite-plugin';
|
|
3
|
-
export * from './vite-cookie-plugin';
|
|
9
|
+
export * from './vite-middleware-plugin';
|
|
4
10
|
export * from './vue-proxy-config';
|
|
5
|
-
export * from './vite-adapter';
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite 中间件代理插件模块
|
|
3
|
+
*
|
|
4
|
+
* 提供基于中间件的 Vite 代理功能,支持自动注入 Cookie。
|
|
5
|
+
* 此实现使用 http-proxy 直接处理请求,确保 Cookie 正确注入。
|
|
6
|
+
*
|
|
7
|
+
* @module vite-middleware-plugin
|
|
8
|
+
*/
|
|
9
|
+
import type { IncomingMessage, ServerResponse } from 'http';
|
|
10
|
+
import { CookieReader } from '../utils/cookie-reader';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Vite 中间件代理 Cookie 插件配置选项
|
|
14
|
+
*/
|
|
15
|
+
export interface ViteMiddlewareProxyOptions {
|
|
16
|
+
/** Cookie 文件路径 */
|
|
17
|
+
cookieFile: string;
|
|
18
|
+
/** 默认代理目标地址 */
|
|
19
|
+
target: string;
|
|
20
|
+
/** 是否启用调试日志 */
|
|
21
|
+
debug?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* 是否使用 Cookie 文件中的 Cookie 注入到请求中
|
|
24
|
+
* - true: 使用文件中的 Cookie(默认)
|
|
25
|
+
* - false: 不注入 Cookie,使用浏览器发送的 Cookie
|
|
26
|
+
*
|
|
27
|
+
* 当使用账号密码登录时,设置为 false,避免覆盖浏览器的登录 Cookie
|
|
28
|
+
*/
|
|
29
|
+
useCookie?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* 代理路径映射表
|
|
32
|
+
* 键:路径前缀,值:代理目标地址
|
|
33
|
+
*/
|
|
34
|
+
proxyMap?: Record<string, string>;
|
|
35
|
+
/**
|
|
36
|
+
* 需要代理的路径前缀列表(使用默认 target)
|
|
37
|
+
*/
|
|
38
|
+
proxyPaths?: string[];
|
|
39
|
+
/**
|
|
40
|
+
* 需要忽略的路径前缀列表(不代理)
|
|
41
|
+
*/
|
|
42
|
+
ignorePaths?: string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 判断路径是否应该被忽略(不代理)
|
|
47
|
+
*/
|
|
48
|
+
function isIgnoredPath(pathname: string, ignorePaths: string[]): boolean {
|
|
49
|
+
return ignorePaths.some(ignored => pathname.startsWith(ignored));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 判断路径是否应该被代理
|
|
54
|
+
*/
|
|
55
|
+
function shouldProxy(pathname: string, proxyPrefixes: string[]): boolean {
|
|
56
|
+
return proxyPrefixes.some(prefix => pathname.startsWith(prefix));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 获取代理目标地址
|
|
61
|
+
*/
|
|
62
|
+
function getProxyTarget(pathname: string, proxyMap: Record<string, string>, defaultTarget: string): string {
|
|
63
|
+
for (const [prefix, target] of Object.entries(proxyMap)) {
|
|
64
|
+
if (pathname.startsWith(prefix)) {
|
|
65
|
+
return target;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return defaultTarget;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 创建 Vite 中间件代理 Cookie 插件
|
|
73
|
+
*
|
|
74
|
+
* @param options - 插件配置选项
|
|
75
|
+
* @returns Vite 插件对象
|
|
76
|
+
*/
|
|
77
|
+
export function viteMiddlewareProxy(options: ViteMiddlewareProxyOptions): any {
|
|
78
|
+
const {
|
|
79
|
+
cookieFile,
|
|
80
|
+
target,
|
|
81
|
+
debug = false,
|
|
82
|
+
useCookie = true,
|
|
83
|
+
proxyMap = {},
|
|
84
|
+
proxyPaths = [],
|
|
85
|
+
ignorePaths = [],
|
|
86
|
+
} = options;
|
|
87
|
+
|
|
88
|
+
// 创建 Cookie 读取器
|
|
89
|
+
const cookieReader = new CookieReader({ cookieFile }, debug);
|
|
90
|
+
let currentCookie = '';
|
|
91
|
+
|
|
92
|
+
// 如果启用 Cookie,读取初始 Cookie
|
|
93
|
+
if (useCookie) {
|
|
94
|
+
currentCookie = cookieReader.readCookie();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 合并所有代理路径前缀
|
|
98
|
+
const allProxyPrefixes = [
|
|
99
|
+
...Object.keys(proxyMap),
|
|
100
|
+
...proxyPaths,
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
name: 'vite-middleware-proxy',
|
|
105
|
+
apply: 'serve',
|
|
106
|
+
|
|
107
|
+
configureServer(server: any) {
|
|
108
|
+
// 延迟加载 http-proxy 以避免启动时的兼容性问题
|
|
109
|
+
const httpProxy = require('http-proxy');
|
|
110
|
+
const proxyServer = httpProxy.createProxyServer({});
|
|
111
|
+
|
|
112
|
+
// 监听 Cookie 文件变化
|
|
113
|
+
if (useCookie && debug) {
|
|
114
|
+
console.log('[ViteMiddlewareProxy] Watching cookie file:', cookieFile);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 添加中间件
|
|
118
|
+
server.middlewares.use((req: IncomingMessage, res: ServerResponse, next: () => void) => {
|
|
119
|
+
const pathname = new URL(req.url || '/', 'http://localhost').pathname;
|
|
120
|
+
|
|
121
|
+
// 检查是否需要忽略
|
|
122
|
+
if (isIgnoredPath(pathname, ignorePaths)) {
|
|
123
|
+
if (debug) {
|
|
124
|
+
console.log('[ViteMiddlewareProxy] Ignoring:', pathname);
|
|
125
|
+
}
|
|
126
|
+
next();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 检查是否需要代理
|
|
131
|
+
if (!shouldProxy(pathname, allProxyPrefixes)) {
|
|
132
|
+
next();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 获取代理目标
|
|
137
|
+
const proxyTarget = getProxyTarget(pathname, proxyMap, target);
|
|
138
|
+
|
|
139
|
+
if (debug) {
|
|
140
|
+
console.log('[ViteMiddlewareProxy] Proxying:', req.method, pathname, '->', proxyTarget);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 如果启用 Cookie,读取最新的 Cookie 并注入
|
|
144
|
+
if (useCookie) {
|
|
145
|
+
currentCookie = cookieReader.readCookie();
|
|
146
|
+
|
|
147
|
+
if (currentCookie) {
|
|
148
|
+
if (debug) {
|
|
149
|
+
console.log('[ViteMiddlewareProxy] Injecting cookie:', `(length: ${currentCookie.length})`);
|
|
150
|
+
}
|
|
151
|
+
(req as any).headers['cookie'] = currentCookie;
|
|
152
|
+
(req as any).headers['Cookie'] = currentCookie;
|
|
153
|
+
} else if (debug) {
|
|
154
|
+
console.log('[ViteMiddlewareProxy] Cookie file is empty');
|
|
155
|
+
}
|
|
156
|
+
} else if (debug) {
|
|
157
|
+
console.log('[ViteMiddlewareProxy] useCookie is false, skipping cookie injection');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// 代理请求
|
|
161
|
+
proxyServer.web(req, res, {
|
|
162
|
+
target: proxyTarget,
|
|
163
|
+
changeOrigin: true,
|
|
164
|
+
secure: false,
|
|
165
|
+
ignorePath: false,
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// 错误处理
|
|
170
|
+
proxyServer.on('error', (err: Error, req: IncomingMessage) => {
|
|
171
|
+
console.error('[ViteMiddlewareProxy] Proxy error:', err.message, 'for', req.url);
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export default viteMiddlewareProxy;
|