@gylautorun/dev-proxy-cookie 1.0.0 → 1.0.2

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.0",
3
+ "version": "1.0.2",
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,16 +80,50 @@ 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;
92
+ /**
93
+ * 是否为开发环境(优先级最高)
94
+ * 设置此参数后,将直接决定是否启用文件监听:
95
+ * - true: 启用监听(开发模式)
96
+ * - false: 禁用监听(生产模式)
97
+ *
98
+ * 使用示例: isDev: process.env.NODE_ENV === 'development'
99
+ */
100
+ isDev?: boolean;
63
101
  }
64
102
 
103
+ /**
104
+ * 自动代理 Cookie 类
105
+ *
106
+ * 提供完整的开发环境代理解决方案,支持 HTTP 和 WebSocket 代理,
107
+ * 自动从文件读取 Cookie 并注入到代理请求中。
108
+ */
65
109
  export class AutoProxyCookie {
110
+ /** 合并后的配置选项 */
66
111
  private options: AutoProxyCookieOptions & { hooks: Required<ProxyHooks> };
112
+ /** 当前 Cookie 值 */
67
113
  private currentCookie: string = '';
114
+ /** Vite 开发服务器实例 */
68
115
  private server: ViteDevServer | null = null;
116
+ /** HTTP 代理服务器实例 */
69
117
  private proxyServer: ProxyServer | null = null;
118
+ /** Cookie 文件监听器 */
70
119
  private watcher: CookieWatcher | null = null;
120
+ /** Cookie 文件读取器 */
71
121
  private cookieReader: CookieReader;
72
122
 
123
+ /**
124
+ * 构造函数
125
+ * @param options - 配置选项
126
+ */
73
127
  constructor(options: AutoProxyCookieOptions) {
74
128
  const defaultHooks: Required<ProxyHooks> = {
75
129
  onProxyReq: () => {},
@@ -91,6 +145,7 @@ export class AutoProxyCookie {
91
145
  autoRestart: false,
92
146
  restartMarkerFile: '.cookie-restart-marker',
93
147
  proxyMap: {},
148
+ proxyPaths: [],
94
149
  ignorePaths: [],
95
150
  ws: true,
96
151
  changeOrigin: true,
@@ -104,9 +159,13 @@ export class AutoProxyCookie {
104
159
  headers: {},
105
160
  ...mergedOptions,
106
161
  };
107
- this.cookieReader = new CookieReader({ cookieFile: options.cookieFile });
162
+ this.cookieReader = new CookieReader({ cookieFile: options.cookieFile }, options.debug ?? false);
108
163
  }
109
164
 
165
+ /**
166
+ * Cookie 变化处理函数
167
+ * @param newCookie - 新的 Cookie 值
168
+ */
110
169
  private handleCookieChange = (newCookie: string): void => {
111
170
  if (newCookie !== this.currentCookie) {
112
171
  this.currentCookie = newCookie;
@@ -124,19 +183,50 @@ export class AutoProxyCookie {
124
183
  }
125
184
  };
126
185
 
186
+ /**
187
+ * 根据请求路径获取代理目标 URL
188
+ * @param req - HTTP 请求对象
189
+ * @returns 代理目标 URL
190
+ */
127
191
  private getProxyUrl(req: IncomingMessage): string {
128
- const pathname = req.url?.split('?')[0] || '/';
192
+ const fullUrl = req.url || '/';
193
+ const pathname = new URL(fullUrl, 'http://localhost').pathname;
129
194
  const proxyMap = this.options.proxyMap || {};
195
+ const proxyPaths = this.options.proxyPaths || [];
130
196
 
197
+ this.log('debug', '[AutoProxyCookie] getProxyUrl - Request path:', pathname);
198
+ this.log('debug', '[AutoProxyCookie] getProxyUrl - Available proxyMap paths:', Object.keys(proxyMap));
199
+ this.log('debug', '[AutoProxyCookie] getProxyUrl - Available proxyPaths:', proxyPaths);
200
+
201
+ // 首先检查 proxyMap(自定义代理目标)
131
202
  for (const [prefix, target] of Object.entries(proxyMap)) {
132
- if (pathname.startsWith(prefix)) {
203
+ const matches = pathname.startsWith(prefix);
204
+ this.log('debug', '[AutoProxyCookie] getProxyUrl - Checking proxyMap:', prefix, '- matches:', matches);
205
+ if (matches) {
206
+ this.log('debug', '[AutoProxyCookie] getProxyUrl - Matched proxyMap:', prefix, '->', target);
133
207
  return target;
134
208
  }
135
209
  }
136
210
 
211
+ // 然后检查 proxyPaths(默认代理到 target)
212
+ for (const prefix of proxyPaths) {
213
+ const matches = pathname.startsWith(prefix);
214
+ this.log('debug', '[AutoProxyCookie] getProxyUrl - Checking proxyPaths:', prefix, '- matches:', matches);
215
+ if (matches) {
216
+ this.log('debug', '[AutoProxyCookie] getProxyUrl - Matched proxyPaths:', prefix, '->', this.options.target);
217
+ return this.options.target;
218
+ }
219
+ }
220
+
221
+ this.log('debug', '[AutoProxyCookie] getProxyUrl - No match found, using default target:', this.options.target);
137
222
  return this.options.target;
138
223
  }
139
224
 
225
+ /**
226
+ * 判断路径是否应该被忽略(不代理)
227
+ * @param pathname - 请求路径
228
+ * @returns 是否忽略
229
+ */
140
230
  private isIgnoredPath(pathname: string): boolean {
141
231
  const ignorePaths = this.options.ignorePaths || [];
142
232
  return ignorePaths.some(ignored =>
@@ -144,9 +234,16 @@ export class AutoProxyCookie {
144
234
  );
145
235
  }
146
236
 
237
+ /**
238
+ * 日志输出函数
239
+ * @param level - 日志级别
240
+ * @param args - 日志参数
241
+ */
147
242
  private log(level: 'debug' | 'info' | 'warn' | 'error', ...args: any[]): void {
148
243
  const levels: Record<string, number> = { debug: 0, info: 1, warn: 2, error: 3 };
149
- const currentLevel = levels[this.options.logLevel || 'info'];
244
+ // 如果设置了 debug: true,则使用 debug 级别
245
+ const effectiveLogLevel = this.options.debug ? 'debug' : (this.options.logLevel || 'info');
246
+ const currentLevel = levels[effectiveLogLevel];
150
247
  const msgLevel = levels[level];
151
248
 
152
249
  if (msgLevel >= currentLevel) {
@@ -160,6 +257,11 @@ export class AutoProxyCookie {
160
257
  }
161
258
  }
162
259
 
260
+ /**
261
+ * 创建代理服务器配置选项
262
+ * @param target - 代理目标地址
263
+ * @returns 代理服务器配置
264
+ */
163
265
  private createProxyOptions(target: string): ServerOptions {
164
266
  const {
165
267
  ws,
@@ -190,9 +292,24 @@ export class AutoProxyCookie {
190
292
  };
191
293
  }
192
294
 
295
+ /**
296
+ * 代理请求处理函数
297
+ * @param proxyReq - 代理请求对象
298
+ * @param req - 原始请求对象
299
+ * @param res - 响应对象
300
+ * @param _options - 服务器选项
301
+ */
193
302
  private handleOnProxyReq = (proxyReq: any, req: IncomingMessage, res: ServerResponse, _options: ServerOptions): void => {
303
+ console.log('[AutoProxyCookie] === handleOnProxyReq START ===');
304
+ console.log('[AutoProxyCookie] Request URL:', req.method, req.url);
305
+ console.log('[AutoProxyCookie] Current cookie:', this.currentCookie ? `(length: ${this.currentCookie.length})` : '(empty)');
306
+
194
307
  if (this.currentCookie) {
308
+ console.log('[AutoProxyCookie] Applying cookie header...');
195
309
  applyDevCookieHeader(proxyReq, this.currentCookie);
310
+ console.log('[AutoProxyCookie] Cookie header applied successfully');
311
+ } else {
312
+ console.log('[AutoProxyCookie] No cookie to apply - currentCookie is empty!');
196
313
  }
197
314
 
198
315
  this.log('debug', '[AutoProxyCookie] Proxy Request:', req.method, req.url);
@@ -204,8 +321,16 @@ export class AutoProxyCookie {
204
321
  this.log('error', '[AutoProxyCookie] onProxyReq hook error:', (err as Error).message);
205
322
  }
206
323
  }
324
+
325
+ console.log('[AutoProxyCookie] === handleOnProxyReq END ===');
207
326
  };
208
327
 
328
+ /**
329
+ * 代理响应处理函数
330
+ * @param proxyRes - 代理响应对象
331
+ * @param req - 原始请求对象
332
+ * @param res - 响应对象
333
+ */
209
334
  private handleOnProxyRes = (proxyRes: any, req: IncomingMessage, res: ServerResponse): void => {
210
335
  const allowedHeaders = [
211
336
  'Content-Type',
@@ -233,6 +358,12 @@ export class AutoProxyCookie {
233
358
  }
234
359
  };
235
360
 
361
+ /**
362
+ * HTTP 代理错误处理函数
363
+ * @param err - 错误对象
364
+ * @param req - 请求对象
365
+ * @param res - 响应对象或 Socket
366
+ */
236
367
  private handleOnError = (err: Error, req: IncomingMessage, res: ServerResponse | net.Socket): void => {
237
368
  this.log('error', '[AutoProxyCookie] Proxy Error:', err.message);
238
369
  this.log('error', '[AutoProxyCookie] URL:', req.url);
@@ -255,6 +386,12 @@ export class AutoProxyCookie {
255
386
  }
256
387
  };
257
388
 
389
+ /**
390
+ * WebSocket 代理错误处理函数
391
+ * @param err - 错误对象
392
+ * @param req - 请求对象
393
+ * @param socket - WebSocket 连接的 Socket
394
+ */
258
395
  private handleOnWsError = (err: Error, req: IncomingMessage, socket: any): void => {
259
396
  this.log('error', '[AutoProxyCookie] WebSocket Proxy Error:', err.message);
260
397
  this.log('error', '[AutoProxyCookie] WebSocket URL:', req.url);
@@ -272,6 +409,10 @@ export class AutoProxyCookie {
272
409
  }
273
410
  };
274
411
 
412
+ /**
413
+ * 初始化代理中间件
414
+ * @param server - Vite 开发服务器实例
415
+ */
275
416
  async setup(server: ViteDevServer): Promise<void> {
276
417
  this.server = server;
277
418
  this.currentCookie = this.cookieReader.readCookie();
@@ -304,30 +445,52 @@ export class AutoProxyCookie {
304
445
  }
305
446
 
306
447
  server.middlewares.use((req: any, res: any, next: any) => {
307
- const pathname = new URL(req.url || '/', 'http://localhost').pathname;
448
+ const fullUrl = req.url || '/';
449
+ const pathname = new URL(fullUrl, 'http://localhost').pathname;
450
+
451
+ // 始终输出请求信息(不仅仅是 debug 模式)
452
+ console.log('[AutoProxyCookie] === Incoming Request ===');
453
+ console.log('[AutoProxyCookie] Method:', req.method);
454
+ console.log('[AutoProxyCookie] Full URL:', fullUrl);
455
+ console.log('[AutoProxyCookie] Pathname:', pathname);
456
+ console.log('[AutoProxyCookie] Headers:', JSON.stringify(req.headers, null, 2));
308
457
 
309
458
  if (this.isIgnoredPath(pathname)) {
459
+ console.log('[AutoProxyCookie] Path ignored, passing to next middleware');
310
460
  next();
311
461
  return;
312
462
  }
313
463
 
464
+ // 读取 cookie 并更新 currentCookie
314
465
  this.currentCookie = this.cookieReader.readCookie();
466
+ console.log('[AutoProxyCookie] Current cookie:', this.currentCookie ? `(length: ${this.currentCookie.length})` : '(empty)');
467
+ if (this.currentCookie) {
468
+ console.log('[AutoProxyCookie] Cookie preview:', this.currentCookie);
469
+ }
470
+
471
+ // 检查是否匹配代理路径(包括 proxyMap 和 proxyPaths)
472
+ const proxyMapKeys = Object.keys(this.options.proxyMap || {});
473
+ const proxyPaths = this.options.proxyPaths || [];
474
+ const allProxyPrefixes = [...proxyMapKeys, ...proxyPaths];
475
+ const matched = allProxyPrefixes.some(prefix => pathname.startsWith(prefix));
476
+ console.log('[AutoProxyCookie] Path matches proxy rules:', matched);
315
477
 
316
478
  if (this.proxyServer) {
317
479
  const target = this.getProxyUrl(req);
318
- if (this.options.debug || this.options.logLevel === 'debug') {
319
- this.log('info', `[AutoProxyCookie] ${pathname} -> ${target}`);
320
- }
480
+ console.log(`[AutoProxyCookie] Proxying ${req.method} ${pathname} -> ${target}`);
321
481
 
322
482
  const proxyOptions = this.createProxyOptions(target);
323
483
 
324
484
  try {
485
+ console.log('[AutoProxyCookie] Calling proxyServer.web...');
325
486
  this.proxyServer.web(req, res, proxyOptions);
487
+ console.log('[AutoProxyCookie] proxyServer.web called successfully');
326
488
  } catch (err: any) {
327
- this.log('error', '[AutoProxyCookie] Proxy web error:', err.message);
489
+ console.error('[AutoProxyCookie] Proxy web error:', err.message);
328
490
  next(err);
329
491
  }
330
492
  } else {
493
+ console.log('[AutoProxyCookie] No proxy server, passing to next middleware');
331
494
  next();
332
495
  }
333
496
  });
@@ -367,16 +530,43 @@ export class AutoProxyCookie {
367
530
  }
368
531
  }
369
532
 
533
+ /**
534
+ * 启动 Cookie 文件监听
535
+ */
370
536
  private startFileWatch(): void {
371
- this.watcher = watchCookieFile(
372
- this.options.cookieFile,
373
- this.handleCookieChange,
374
- (error) => {
375
- this.log('error', '[AutoProxyCookie] File watch error:', error.message);
537
+ // 判断是否应该启用监听
538
+ // isDev 参数优先级最高
539
+ let shouldWatch: boolean;
540
+
541
+ if (this.options.isDev !== undefined) {
542
+ shouldWatch = this.options.isDev;
543
+ if (this.options.debug) {
544
+ console.log(`[AutoProxyCookie] isDev=${this.options.isDev}, ${shouldWatch ? 'enabling' : 'disabling'} watch`);
376
545
  }
377
- );
546
+ } else {
547
+ // 默认启用监听(因为 AutoProxyCookie 主要用于开发环境)
548
+ shouldWatch = true;
549
+ if (this.options.debug) {
550
+ console.log('[AutoProxyCookie] Default behavior: enabling watch (dev mode)');
551
+ }
552
+ }
553
+
554
+ if (shouldWatch) {
555
+ this.watcher = watchCookieFile(
556
+ this.options.cookieFile,
557
+ this.handleCookieChange,
558
+ (error) => {
559
+ this.log('error', '[AutoProxyCookie] File watch error:', error.message);
560
+ }
561
+ );
562
+ } else if (this.options.debug) {
563
+ console.log('[AutoProxyCookie] File watch disabled');
564
+ }
378
565
  }
379
566
 
567
+ /**
568
+ * 停止代理服务
569
+ */
380
570
  stop(): void {
381
571
  if (this.watcher) {
382
572
  this.watcher.stop();
@@ -389,11 +579,20 @@ export class AutoProxyCookie {
389
579
  this.log('info', '[AutoProxyCookie] Stopped');
390
580
  }
391
581
 
582
+ /**
583
+ * 获取当前 Cookie 值
584
+ * @returns 当前 Cookie 字符串
585
+ */
392
586
  getCurrentCookie(): string {
393
587
  return this.currentCookie;
394
588
  }
395
589
  }
396
590
 
591
+ /**
592
+ * 创建 AutoProxyCookie 实例
593
+ * @param options - 配置选项
594
+ * @returns AutoProxyCookie 实例
595
+ */
397
596
  export function createAutoProxyCookie(options: AutoProxyCookieOptions): AutoProxyCookie {
398
597
  return new AutoProxyCookie(options);
399
598
  }
@@ -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,158 @@
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
+ * 代理路径映射表
24
+ * 键:路径前缀,值:代理目标地址
25
+ */
26
+ proxyMap?: Record<string, string>;
27
+ /**
28
+ * 需要代理的路径前缀列表(使用默认 target)
29
+ */
30
+ proxyPaths?: string[];
31
+ /**
32
+ * 需要忽略的路径前缀列表(不代理)
33
+ */
34
+ ignorePaths?: string[];
35
+ }
36
+
37
+ /**
38
+ * 判断路径是否应该被忽略(不代理)
39
+ */
40
+ function isIgnoredPath(pathname: string, ignorePaths: string[]): boolean {
41
+ return ignorePaths.some(ignored => pathname.startsWith(ignored));
42
+ }
43
+
44
+ /**
45
+ * 判断路径是否应该被代理
46
+ */
47
+ function shouldProxy(pathname: string, proxyPrefixes: string[]): boolean {
48
+ return proxyPrefixes.some(prefix => pathname.startsWith(prefix));
49
+ }
50
+
51
+ /**
52
+ * 获取代理目标地址
53
+ */
54
+ function getProxyTarget(pathname: string, proxyMap: Record<string, string>, defaultTarget: string): string {
55
+ for (const [prefix, target] of Object.entries(proxyMap)) {
56
+ if (pathname.startsWith(prefix)) {
57
+ return target;
58
+ }
59
+ }
60
+ return defaultTarget;
61
+ }
62
+
63
+ /**
64
+ * 创建 Vite 中间件代理 Cookie 插件
65
+ *
66
+ * @param options - 插件配置选项
67
+ * @returns Vite 插件对象
68
+ */
69
+ export function viteMiddlewareProxy(options: ViteMiddlewareProxyOptions): any {
70
+ const {
71
+ cookieFile,
72
+ target,
73
+ debug = false,
74
+ proxyMap = {},
75
+ proxyPaths = [],
76
+ ignorePaths = [],
77
+ } = options;
78
+
79
+ // 创建 Cookie 读取器
80
+ const cookieReader = new CookieReader({ cookieFile }, debug);
81
+ let currentCookie = cookieReader.readCookie();
82
+
83
+ // 合并所有代理路径前缀
84
+ const allProxyPrefixes = [
85
+ ...Object.keys(proxyMap),
86
+ ...proxyPaths,
87
+ ];
88
+
89
+ return {
90
+ name: 'vite-middleware-proxy',
91
+ apply: 'serve',
92
+
93
+ configureServer(server: any) {
94
+ // 延迟加载 http-proxy 以避免启动时的兼容性问题
95
+ const httpProxy = require('http-proxy');
96
+ const proxyServer = httpProxy.createProxyServer({});
97
+
98
+ // 监听 Cookie 文件变化
99
+ if (debug) {
100
+ console.log('[ViteMiddlewareProxy] Watching cookie file:', cookieFile);
101
+ }
102
+
103
+ // 添加中间件
104
+ server.middlewares.use((req: IncomingMessage, res: ServerResponse, next: () => void) => {
105
+ const pathname = new URL(req.url || '/', 'http://localhost').pathname;
106
+
107
+ // 检查是否需要忽略
108
+ if (isIgnoredPath(pathname, ignorePaths)) {
109
+ if (debug) {
110
+ console.log('[ViteMiddlewareProxy] Ignoring:', pathname);
111
+ }
112
+ next();
113
+ return;
114
+ }
115
+
116
+ // 检查是否需要代理
117
+ if (!shouldProxy(pathname, allProxyPrefixes)) {
118
+ next();
119
+ return;
120
+ }
121
+
122
+ // 获取代理目标
123
+ const proxyTarget = getProxyTarget(pathname, proxyMap, target);
124
+
125
+ if (debug) {
126
+ console.log('[ViteMiddlewareProxy] Proxying:', req.method, pathname, '->', proxyTarget);
127
+ }
128
+
129
+ // 读取最新的 Cookie
130
+ currentCookie = cookieReader.readCookie();
131
+
132
+ // 注入 Cookie
133
+ if (currentCookie) {
134
+ if (debug) {
135
+ console.log('[ViteMiddlewareProxy] Injecting cookie:', `(length: ${currentCookie.length})`);
136
+ }
137
+ (req as any).headers['cookie'] = currentCookie;
138
+ (req as any).headers['Cookie'] = currentCookie;
139
+ }
140
+
141
+ // 代理请求
142
+ proxyServer.web(req, res, {
143
+ target: proxyTarget,
144
+ changeOrigin: true,
145
+ secure: false,
146
+ ignorePath: false,
147
+ });
148
+ });
149
+
150
+ // 错误处理
151
+ proxyServer.on('error', (err: Error, req: IncomingMessage) => {
152
+ console.error('[ViteMiddlewareProxy] Proxy error:', err.message, 'for', req.url);
153
+ });
154
+ },
155
+ };
156
+ }
157
+
158
+ export default viteMiddlewareProxy;