@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gylautorun/dev-proxy-cookie",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "开发环境代理Cookie注入工具,支持文件监听自动重载和自动代理",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
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
- * Replace outbound Cookie with the dev file cookie.
3
- * Browsers send `Cookie` when `withCredentials` is true; merging with file cookie often yields duplicate
4
- * names (e.g. two JSESSIONID) and many servers honor the first value — the stale browser session.
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
- if (!cookie) return;
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 pathname = req.url?.split('?')[0] || '/';
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
- if (pathname.startsWith(prefix)) {
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
- const currentLevel = levels[this.options.logLevel || 'info'];
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
- if (this.currentCookie) {
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 pathname = new URL(req.url || '/', 'http://localhost').pathname;
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
- this.currentCookie = this.cookieReader.readCookie();
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
- if (this.options.debug || this.options.logLevel === 'debug') {
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
- this.log('error', '[AutoProxyCookie] Proxy web error:', err.message);
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
  }
@@ -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;