whistle.pastekitlab 1.3.1 → 1.3.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.
Files changed (3) hide show
  1. package/back.js +107 -229
  2. package/index.js +10 -184
  3. package/package.json +1 -1
package/back.js CHANGED
@@ -1,11 +1,8 @@
1
1
  /**
2
- * Whistle Plugin for PasteKit Lab
3
- *
4
- * 功能:拦截匹配的域名请求,通过 WebSocket 发送到 Chrome 插件
2
+ * Whistle Plugin for PasteKit Lab(最终版 - 只监听)
5
3
  */
6
4
 
7
5
  const WebSocket = require('ws');
8
- const http = require('http');
9
6
  const fs = require('fs');
10
7
  const path = require('path');
11
8
 
@@ -13,297 +10,178 @@ const path = require('path');
13
10
  const CONFIG = {
14
11
  wsPort: 8889,
15
12
  logEnabled: true,
16
- logFile: path.join(__dirname, 'plugin.log') // 日志文件路径
13
+ logFile: path.join(__dirname, 'plugin.log')
17
14
  };
18
15
 
19
16
  // ==================== 全局状态 ====================
20
17
  let wss = null;
21
- let httpServer = null;
22
18
  let clients = new Map();
23
19
  let clientIdCounter = 0;
24
- let domainConfig = []; // 存储从 Chrome 插件接收的域名配置
20
+ let domainConfig = [];
25
21
 
26
- // ==================== 工具函数 ====================
22
+ // ==================== 日志 ====================
27
23
  function log(message) {
28
- if (CONFIG.logEnabled) {
29
- const timestamp = new Date().toISOString();
30
- const logMessage = `[${timestamp}] [Whistle.pastekitlab] ${message}\n`;
31
-
32
- // 输出到控制台
33
- console.log('[Whistle.pastekitlab]', message);
34
-
35
- // 追加到日志文件
36
- try {
37
- fs.appendFileSync(CONFIG.logFile, logMessage, 'utf8');
38
- } catch (e) {
39
- // 忽略文件写入错误
40
- }
41
- }
24
+ if (!CONFIG.logEnabled) return;
25
+
26
+ const timestamp = new Date().toISOString();
27
+ const logMessage = `[${timestamp}] ${message}\n`;
28
+
29
+ console.log('[pastekitlab]', message);
30
+
31
+ try {
32
+ fs.appendFileSync(CONFIG.logFile, logMessage, 'utf8');
33
+ } catch (e) {}
42
34
  }
43
35
 
36
+ // ==================== 工具 ====================
44
37
  function bufferToString(buffer) {
45
38
  try {
46
39
  if (!buffer) return null;
47
40
  const result = buffer.toString('utf8');
48
- if (/[^\x20-\x7E\r\n\t]/.test(result)) {
49
- return null;
50
- }
41
+ if (/[^\x20-\x7E\r\n\t]/.test(result)) return null;
51
42
  return result;
52
- } catch (e) {
43
+ } catch {
53
44
  return null;
54
45
  }
55
46
  }
56
47
 
57
48
  function bufferToBase64(buffer) {
58
49
  try {
59
- if (!buffer) return null;
60
- return buffer.toString('base64');
61
- } catch (e) {
50
+ return buffer ? buffer.toString('base64') : null;
51
+ } catch {
62
52
  return null;
63
53
  }
64
54
  }
65
55
 
66
56
  function generateId() {
67
- return 'req_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
57
+ return 'req_' + Date.now() + '_' + Math.random().toString(36).slice(2);
68
58
  }
69
59
 
70
- // ==================== 检查 URL 是否匹配配置的域名 ====================
60
+ // ==================== 域名过滤 ====================
71
61
  function shouldIntercept(url) {
72
- if (!domainConfig || domainConfig.length === 0) {
73
- return false;
74
- }
75
-
62
+ if (!domainConfig.length) return true;
63
+
76
64
  try {
77
- const urlObj = new URL(url);
78
- const hostname = urlObj.hostname;
79
-
65
+ const { hostname } = new URL(url);
66
+
80
67
  return domainConfig.some(domain => {
81
- // 精确匹配
82
68
  if (hostname === domain) return true;
83
- // 通配符匹配 *.example.com
84
69
  if (domain.startsWith('*.')) {
85
- const baseDomain = domain.substring(2);
86
- return hostname.endsWith('.' + baseDomain) || hostname === baseDomain;
70
+ const base = domain.slice(2);
71
+ return hostname.endsWith('.' + base) || hostname === base;
87
72
  }
88
73
  return false;
89
74
  });
90
- } catch (e) {
75
+ } catch {
91
76
  return false;
92
77
  }
93
78
  }
94
79
 
95
- // ==================== 启动 WebSocket 服务器 ====================
80
+ // ==================== WebSocket ====================
96
81
  function startWebSocketServer() {
97
82
  if (wss) return;
98
83
 
99
- try {
100
- wss = new WebSocket.Server({
101
- port: CONFIG.wsPort,
102
- path: '/ws',
103
- maxPayload: 10 * 1024 * 1024
104
- });
84
+ wss = new WebSocket.Server({
85
+ port: CONFIG.wsPort,
86
+ path: '/ws'
87
+ });
105
88
 
106
- wss.on('connection', (ws, req) => {
107
- const clientId = ++clientIdCounter;
108
- const clientIp = req.socket.remoteAddress;
109
-
110
- log(`客户端 ${clientId} 已连接 (${clientIp})`);
111
-
112
- clients.set(clientId, {
113
- id: clientId,
114
- ws: ws,
115
- ip: clientIp,
116
- connectedAt: Date.now()
117
- });
118
-
119
- // 发送欢迎消息
120
- ws.send(JSON.stringify({
121
- msgType: 'connected',
122
- clientId: clientId,
123
- timestamp: Date.now()
124
- }));
125
-
126
- // 设置心跳
127
- ws.isAlive = true;
128
- ws.on('pong', () => {
129
- ws.isAlive = true;
130
- });
131
-
132
- // 处理客户端消息(接收 domain 配置)
133
- ws.on('message', (data) => {
134
- try {
135
- const message = JSON.parse(data.toString());
136
-
137
- if (message.msgType === 'domain') {
138
- // 更新域名配置
139
- domainConfig = message.domain || [];
140
- log(`收到域名配置: ${domainConfig.length} 个域名`);
141
-
142
- // 确认配置已接收
143
- ws.send(JSON.stringify({
144
- msgType: 'domainConfigReceived',
145
- domainCount: domainConfig.length,
146
- timestamp: Date.now()
147
- }));
148
- }
149
- } catch (error) {
150
- log(`消息解析失败: ${error.message}`);
151
- }
152
- });
89
+ wss.on('connection', (ws, req) => {
90
+ const id = ++clientIdCounter;
153
91
 
154
- ws.on('close', (code, reason) => {
155
- log(`客户端 ${clientId} 已断开 (code: ${code})`);
156
- clients.delete(clientId);
157
- });
92
+ clients.set(id, ws);
93
+ log(`客户端连接: ${id}`);
158
94
 
159
- ws.on('error', (error) => {
160
- log(`客户端 ${clientId} 错误: ${error.message}`);
161
- clients.delete(clientId);
162
- });
163
- });
95
+ ws.send(JSON.stringify({
96
+ msgType: 'connected',
97
+ clientId: id
98
+ }));
164
99
 
165
- // 定期心跳检测
166
- // const heartbeat = setInterval(() => {
167
- // wss.clients.forEach((ws) => {
168
- // if (ws.isAlive === false) {
169
- // return ws.terminate();
170
- // }
171
- // ws.isAlive = false;
172
- // ws.ping();
173
- // });
174
- // }, 30000);
175
-
176
- wss.on('close', () => {
177
- clearInterval(heartbeat);
178
- });
100
+ ws.on('message', (data) => {
101
+ try {
102
+ const msg = JSON.parse(data.toString());
179
103
 
180
- wss.on('error', (error) => {
181
- log(`WebSocket 服务器错误: ${error.message}`);
104
+ if (msg.msgType === 'domain') {
105
+ domainConfig = msg.domain || [];
106
+ log(`更新域名配置: ${domainConfig.join(',')}`);
107
+ }
108
+ } catch {}
182
109
  });
183
110
 
184
- log(`WebSocket 服务器已启动: ws://127.0.0.1:${CONFIG.wsPort}/ws`);
111
+ ws.on('close', () => {
112
+ clients.delete(id);
113
+ log(`客户端断开: ${id}`);
114
+ });
115
+ });
185
116
 
186
- } catch (error) {
187
- log(`启动 WebSocket 服务器失败: ${error.message}`);
188
- }
117
+ log(`WebSocket 启动: ws://127.0.0.1:${CONFIG.wsPort}/ws`);
189
118
  }
190
119
 
191
- // ==================== 广播消息给所有客户端 ====================
192
- function broadcastToClients(msgType, data) {
193
- if (clients.size === 0) return;
120
+ function broadcast(type, data) {
121
+ if (!clients.size) return;
194
122
 
195
- const message = JSON.stringify({
196
- msgType: msgType,
123
+ const msg = JSON.stringify({
124
+ msgType: type,
197
125
  ...data,
198
126
  timestamp: Date.now()
199
127
  });
200
128
 
201
- clients.forEach((client, clientId) => {
202
- if (client.ws.readyState === WebSocket.OPEN) {
203
- try {
204
- client.ws.send(message);
205
- } catch (error) {
206
- log(`向客户端 ${clientId} 发送失败`);
207
- }
129
+ clients.forEach(ws => {
130
+ if (ws.readyState === WebSocket.OPEN) {
131
+ ws.send(msg);
208
132
  }
209
133
  });
210
134
  }
211
135
 
212
- // ==================== Whistle 插件导出 ====================
213
- module.exports = {
214
- /**
215
- * 处理请求阶段 - beforeRequest
216
- */
217
- '*': async function(req, res, next) {
218
- // 确保 WebSocket 服务器已启动
219
- if (!wss) {
220
- startWebSocketServer();
221
- }
136
+ // ==================== Whistle 插件 ====================
137
+ module.exports = (server, options) => {
138
+ log('插件已启动(server.on 模式)');
139
+ startWebSocketServer();
222
140
 
223
- log(`接收到请求${req}`)
224
- const url = req.fullUrl || req.url;
225
-
226
- // 检查是否匹配配置的域名
227
- // if (!shouldIntercept(url)) {
228
- // return next();
229
- // }
230
-
231
- const method = req.method;
232
- const headers = req.headers;
141
+ // ==================== 请求监听 ====================
142
+ server.on('request', (req, res) => {
143
+ try {
144
+ const url = req.fullUrl || req.url;
145
+ if (!shouldIntercept(url)) return;
233
146
 
234
- // 获取请求体
235
- let requestBody = null;
236
- let requestBodyBase64 = null;
147
+ const data = {
148
+ eventId: generateId(),
149
+ url,
150
+ method: req.method,
151
+ headers: req.headers,
152
+ body: bufferToString(req._reqBody),
153
+ bodyBase64: bufferToBase64(req._reqBody)
154
+ };
237
155
 
238
- if (req._reqBody) {
239
- requestBody = bufferToString(req._reqBody);
240
- requestBodyBase64 = bufferToBase64(req._reqBody);
241
- }
156
+ log(`REQ ${req.method} ${url}`);
157
+ broadcast('REQUEST', data);
242
158
 
243
- // 构建请求数据
244
- const requestData = {
245
- eventId: generateId(),
246
- url: url,
247
- method: method,
248
- requestHeaders: headers,
249
- requestBody: requestBody,
250
- requestBodyBase64: requestBodyBase64,
251
- timestamp: Date.now()
252
- };
253
-
254
- log(`拦截请求: ${method} ${url}`);
255
-
256
- // 发送请求数据给 Chrome 插件
257
- broadcastToClients('MOCK', requestData);
258
-
259
- next();
260
- },
261
-
262
- /**
263
- * 处理响应阶段 - afterResponse
264
- */
265
- '* response': async function(req, res, next) {
266
- const url = req.fullUrl || req.url;
267
- log(`接收到响应${req}`)
268
-
269
- // // 检查是否匹配配置的域名
270
- // if (!shouldIntercept(url)) {
271
- // return next();
272
- // }
273
-
274
- const statusCode = res.statusCode;
275
- const headers = res.headers;
276
-
277
- // 获取响应体
278
- let responseBody = null;
279
- let responseBodyBase64 = null;
280
-
281
- if (res._resBody) {
282
- responseBody = bufferToString(res._resBody);
283
- responseBodyBase64 = bufferToBase64(res._resBody);
159
+ } catch (e) {
160
+ log('request error: ' + e.message);
284
161
  }
162
+ });
285
163
 
286
- // 构建响应数据
287
- const responseData = {
288
- eventId: generateId(),
289
- url: url,
290
- method: req.method,
291
- statusCode: statusCode,
292
- responseHeaders: headers,
293
- responseBody: responseBody,
294
- responseBodyBase64: responseBodyBase64,
295
- timestamp: Date.now()
296
- };
297
-
298
- log(`拦截响应: ${statusCode} ${url}`);
299
-
300
- // 发送响应数据给 Chrome 插件
301
- broadcastToClients('RESPONSE', responseData);
302
- next();
303
- }
304
-
305
- };
164
+ // ==================== 响应监听 ====================
165
+ server.on('response', (req, res) => {
166
+ try {
167
+ const url = req.fullUrl || req.url;
168
+ if (!shouldIntercept(url)) return;
169
+
170
+ const data = {
171
+ eventId: generateId(),
172
+ url,
173
+ method: req.method,
174
+ statusCode: res.statusCode,
175
+ headers: res.headers,
176
+ body: bufferToString(res._resBody),
177
+ bodyBase64: bufferToBase64(res._resBody)
178
+ };
179
+
180
+ log(`RES ${res.statusCode} ${url}`);
181
+ broadcast('RESPONSE', data);
306
182
 
307
- // ==================== 自动初始化 ====================
308
- log('插件已加载');
309
- startWebSocketServer();
183
+ } catch (e) {
184
+ log('response error: ' + e.message);
185
+ }
186
+ });
187
+ };
package/index.js CHANGED
@@ -1,187 +1,13 @@
1
- /**
2
- * Whistle Plugin for PasteKit Lab(最终版 - 只监听)
3
- */
4
-
5
- const WebSocket = require('ws');
6
- const fs = require('fs');
7
- const path = require('path');
8
-
9
- // ==================== 配置 ====================
10
- const CONFIG = {
11
- wsPort: 8889,
12
- logEnabled: true,
13
- logFile: path.join(__dirname, 'plugin.log')
14
- };
15
-
16
- // ==================== 全局状态 ====================
17
- let wss = null;
18
- let clients = new Map();
19
- let clientIdCounter = 0;
20
- let domainConfig = [];
21
-
22
- // ==================== 日志 ====================
23
- function log(message) {
24
- if (!CONFIG.logEnabled) return;
25
-
26
- const timestamp = new Date().toISOString();
27
- const logMessage = `[${timestamp}] ${message}\n`;
28
-
29
- console.log('[pastekitlab]', message);
30
-
31
- try {
32
- fs.appendFileSync(CONFIG.logFile, logMessage, 'utf8');
33
- } catch (e) {}
34
- }
35
-
36
- // ==================== 工具 ====================
37
- function bufferToString(buffer) {
38
- try {
39
- if (!buffer) return null;
40
- const result = buffer.toString('utf8');
41
- if (/[^\x20-\x7E\r\n\t]/.test(result)) return null;
42
- return result;
43
- } catch {
44
- return null;
45
- }
46
- }
47
-
48
- function bufferToBase64(buffer) {
49
- try {
50
- return buffer ? buffer.toString('base64') : null;
51
- } catch {
52
- return null;
53
- }
54
- }
55
-
56
- function generateId() {
57
- return 'req_' + Date.now() + '_' + Math.random().toString(36).slice(2);
58
- }
59
-
60
- // ==================== 域名过滤 ====================
61
- function shouldIntercept(url) {
62
- if (!domainConfig.length) return true;
63
-
64
- try {
65
- const { hostname } = new URL(url);
66
-
67
- return domainConfig.some(domain => {
68
- if (hostname === domain) return true;
69
- if (domain.startsWith('*.')) {
70
- const base = domain.slice(2);
71
- return hostname.endsWith('.' + base) || hostname === base;
72
- }
73
- return false;
74
- });
75
- } catch {
76
- return false;
77
- }
78
- }
79
-
80
- // ==================== WebSocket ====================
81
- function startWebSocketServer() {
82
- if (wss) return;
83
-
84
- wss = new WebSocket.Server({
85
- port: CONFIG.wsPort,
86
- path: '/ws'
87
- });
88
-
89
- wss.on('connection', (ws, req) => {
90
- const id = ++clientIdCounter;
91
-
92
- clients.set(id, ws);
93
- log(`客户端连接: ${id}`);
94
-
95
- ws.send(JSON.stringify({
96
- msgType: 'connected',
97
- clientId: id
98
- }));
99
-
100
- ws.on('message', (data) => {
101
- try {
102
- const msg = JSON.parse(data.toString());
103
-
104
- if (msg.msgType === 'domain') {
105
- domainConfig = msg.domain || [];
106
- log(`更新域名配置: ${domainConfig.join(',')}`);
107
- }
108
- } catch {}
109
- });
110
-
111
- ws.on('close', () => {
112
- clients.delete(id);
113
- log(`客户端断开: ${id}`);
114
- });
115
- });
116
-
117
- log(`WebSocket 启动: ws://127.0.0.1:${CONFIG.wsPort}/ws`);
118
- }
119
-
120
- function broadcast(type, data) {
121
- if (!clients.size) return;
122
-
123
- const msg = JSON.stringify({
124
- msgType: type,
125
- ...data,
126
- timestamp: Date.now()
127
- });
128
-
129
- clients.forEach(ws => {
130
- if (ws.readyState === WebSocket.OPEN) {
131
- ws.send(msg);
132
- }
133
- });
134
- }
135
-
136
- // ==================== Whistle 插件 ====================
137
1
  module.exports = (server, options) => {
138
- log('插件已启动(server.on 模式)');
139
- startWebSocketServer();
140
-
141
- // ==================== 请求监听 ====================
142
- server.on('request', (req, res) => {
143
- try {
144
- const url = req.fullUrl || req.url;
145
- if (!shouldIntercept(url)) return;
146
-
147
- const data = {
148
- eventId: generateId(),
149
- url,
150
- method: req.method,
151
- headers: req.headers,
152
- body: bufferToString(req._reqBody),
153
- bodyBase64: bufferToBase64(req._reqBody)
154
- };
155
-
156
- log(`REQ ${req.method} ${url}`);
157
- broadcast('REQUEST', data);
158
-
159
- } catch (e) {
160
- log('request error: ' + e.message);
161
- }
162
- });
163
-
164
- // ==================== 响应监听 ====================
165
- server.on('response', (req, res) => {
166
- try {
167
- const url = req.fullUrl || req.url;
168
- if (!shouldIntercept(url)) return;
169
-
170
- const data = {
171
- eventId: generateId(),
172
- url,
173
- method: req.method,
174
- statusCode: res.statusCode,
175
- headers: res.headers,
176
- body: bufferToString(res._resBody),
177
- bodyBase64: bufferToBase64(res._resBody)
178
- };
179
-
180
- log(`RES ${res.statusCode} ${url}`);
181
- broadcast('RESPONSE', data);
182
-
183
- } catch (e) {
184
- log('response error: ' + e.message);
2
+ console.log('>>> plugin loaded');
3
+ // server.on('request', (req, res) => {
4
+ // console.log('>>> takeover:', req.fullUrl);
5
+ // // 直接用
6
+ // });
7
+ return {
8
+ request: (req, res,next) => {
9
+ console.log('>>> takeover:', req.fullUrl);
10
+ return next();
185
11
  }
186
- });
12
+ };
187
13
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whistle.pastekitlab",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "Whistle plugin for PasteKit Lab - Intercepts requests and sends them to requestlistviewer via WebSocket",
5
5
  "main": "index.js",
6
6
  "scripts": {