whistle.pastekitlab 1.2.5 → 1.3.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.
Files changed (2) hide show
  1. package/index.js +185 -6
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,8 +1,187 @@
1
- console.log('>>> testplugin loaded!');
2
- module.exports = (server, options) => {
3
- console.log('>>> plugin 333 loaded');
4
- server.on('request', (req, res, next) => {
5
- console.log('>>> [GLOBAL]', req.fullUrl);
6
- if (next) next();
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
+ module.exports = (server) => {
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);
185
+ }
7
186
  });
8
187
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whistle.pastekitlab",
3
- "version": "1.2.5",
3
+ "version": "1.3.0",
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": {