whistle.pastekitlab 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/index.js +51 -0
- package/options.html +468 -0
- package/package.json +3 -2
- package/rules.txt +8 -0
- package/config-whistle.sh +0 -19
package/index.js
CHANGED
|
@@ -5,15 +5,20 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const WebSocket = require('ws');
|
|
8
|
+
const http = require('http');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
8
11
|
|
|
9
12
|
// ==================== 配置 ====================
|
|
10
13
|
const CONFIG = {
|
|
11
14
|
wsPort: 8889,
|
|
15
|
+
httpPort: 8890,
|
|
12
16
|
logEnabled: true
|
|
13
17
|
};
|
|
14
18
|
|
|
15
19
|
// ==================== 全局状态 ====================
|
|
16
20
|
let wss = null;
|
|
21
|
+
let httpServer = null;
|
|
17
22
|
let clients = new Map();
|
|
18
23
|
let clientIdCounter = 0;
|
|
19
24
|
let domainConfig = []; // 存储从 Chrome 插件接收的域名配置
|
|
@@ -145,11 +150,57 @@ function startWebSocketServer() {
|
|
|
145
150
|
});
|
|
146
151
|
|
|
147
152
|
log(`WebSocket 服务器已启动: ws://127.0.0.1:${CONFIG.wsPort}/ws`);
|
|
153
|
+
|
|
154
|
+
// 启动 HTTP 服务器(Options 页面)
|
|
155
|
+
startHttpServer();
|
|
148
156
|
} catch (error) {
|
|
149
157
|
log(`启动 WebSocket 服务器失败: ${error.message}`);
|
|
150
158
|
}
|
|
151
159
|
}
|
|
152
160
|
|
|
161
|
+
// ==================== 启动 HTTP 服务器 ====================
|
|
162
|
+
function startHttpServer() {
|
|
163
|
+
if (httpServer) return;
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
httpServer = http.createServer((req, res) => {
|
|
167
|
+
const url = req.url;
|
|
168
|
+
|
|
169
|
+
// Options 页面
|
|
170
|
+
if (url === '/' || url === '/options' || url === '/options.html') {
|
|
171
|
+
const optionsPath = path.join(__dirname, 'options.html');
|
|
172
|
+
fs.readFile(optionsPath, 'utf8', (err, data) => {
|
|
173
|
+
if (err) {
|
|
174
|
+
res.writeHead(500);
|
|
175
|
+
res.end('Internal Server Error');
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
179
|
+
res.end(data);
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
res.writeHead(404);
|
|
185
|
+
res.end('Not Found');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
httpServer.listen(CONFIG.httpPort, () => {
|
|
189
|
+
log(`Options 页面已启动: http://127.0.0.1:${CONFIG.httpPort}`);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
httpServer.on('error', (error) => {
|
|
193
|
+
if (error.code === 'EADDRINUSE') {
|
|
194
|
+
log(`端口 ${CONFIG.httpPort} 已被占用`);
|
|
195
|
+
} else {
|
|
196
|
+
log(`HTTP 服务器错误: ${error.message}`);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
} catch (error) {
|
|
200
|
+
log(`启动 HTTP 服务器失败: ${error.message}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
153
204
|
// ==================== 广播消息给所有客户端 ====================
|
|
154
205
|
function broadcastToClients(msgType, data) {
|
|
155
206
|
if (clients.size === 0) return;
|
package/options.html
ADDED
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>PasteKit Lab - 域名配置</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
16
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
17
|
+
min-height: 100vh;
|
|
18
|
+
padding: 20px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.container {
|
|
22
|
+
max-width: 900px;
|
|
23
|
+
margin: 0 auto;
|
|
24
|
+
background: white;
|
|
25
|
+
border-radius: 12px;
|
|
26
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
27
|
+
overflow: hidden;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.header {
|
|
31
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
32
|
+
color: white;
|
|
33
|
+
padding: 30px;
|
|
34
|
+
text-align: center;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.header h1 {
|
|
38
|
+
font-size: 28px;
|
|
39
|
+
margin-bottom: 10px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.header p {
|
|
43
|
+
opacity: 0.9;
|
|
44
|
+
font-size: 14px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.content {
|
|
48
|
+
padding: 30px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.section {
|
|
52
|
+
margin-bottom: 30px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.section-title {
|
|
56
|
+
font-size: 18px;
|
|
57
|
+
font-weight: 600;
|
|
58
|
+
color: #333;
|
|
59
|
+
margin-bottom: 15px;
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
gap: 8px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.section-title::before {
|
|
66
|
+
content: '';
|
|
67
|
+
width: 4px;
|
|
68
|
+
height: 20px;
|
|
69
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
70
|
+
border-radius: 2px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.domain-input-group {
|
|
74
|
+
display: flex;
|
|
75
|
+
gap: 10px;
|
|
76
|
+
margin-bottom: 15px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.domain-input {
|
|
80
|
+
flex: 1;
|
|
81
|
+
padding: 12px 16px;
|
|
82
|
+
border: 2px solid #e0e0e0;
|
|
83
|
+
border-radius: 8px;
|
|
84
|
+
font-size: 14px;
|
|
85
|
+
transition: all 0.3s;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.domain-input:focus {
|
|
89
|
+
outline: none;
|
|
90
|
+
border-color: #667eea;
|
|
91
|
+
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.btn {
|
|
95
|
+
padding: 12px 24px;
|
|
96
|
+
border: none;
|
|
97
|
+
border-radius: 8px;
|
|
98
|
+
font-size: 14px;
|
|
99
|
+
font-weight: 600;
|
|
100
|
+
cursor: pointer;
|
|
101
|
+
transition: all 0.3s;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.btn-primary {
|
|
105
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
106
|
+
color: white;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.btn-primary:hover {
|
|
110
|
+
transform: translateY(-2px);
|
|
111
|
+
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.btn-danger {
|
|
115
|
+
background: #ff4757;
|
|
116
|
+
color: white;
|
|
117
|
+
padding: 6px 12px;
|
|
118
|
+
font-size: 12px;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.btn-danger:hover {
|
|
122
|
+
background: #ff3838;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.domain-list {
|
|
126
|
+
list-style: none;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.domain-item {
|
|
130
|
+
display: flex;
|
|
131
|
+
justify-content: space-between;
|
|
132
|
+
align-items: center;
|
|
133
|
+
padding: 12px 16px;
|
|
134
|
+
background: #f8f9fa;
|
|
135
|
+
border-radius: 8px;
|
|
136
|
+
margin-bottom: 8px;
|
|
137
|
+
transition: all 0.3s;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.domain-item:hover {
|
|
141
|
+
background: #e9ecef;
|
|
142
|
+
transform: translateX(5px);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.domain-text {
|
|
146
|
+
font-size: 14px;
|
|
147
|
+
color: #333;
|
|
148
|
+
font-family: 'Courier New', monospace;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.empty-state {
|
|
152
|
+
text-align: center;
|
|
153
|
+
padding: 40px;
|
|
154
|
+
color: #999;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.empty-state svg {
|
|
158
|
+
width: 64px;
|
|
159
|
+
height: 64px;
|
|
160
|
+
margin-bottom: 15px;
|
|
161
|
+
opacity: 0.3;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.status-bar {
|
|
165
|
+
background: #f8f9fa;
|
|
166
|
+
padding: 15px 20px;
|
|
167
|
+
border-top: 1px solid #e0e0e0;
|
|
168
|
+
display: flex;
|
|
169
|
+
justify-content: space-between;
|
|
170
|
+
align-items: center;
|
|
171
|
+
font-size: 13px;
|
|
172
|
+
color: #666;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.status-indicator {
|
|
176
|
+
display: flex;
|
|
177
|
+
align-items: center;
|
|
178
|
+
gap: 8px;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.status-dot {
|
|
182
|
+
width: 8px;
|
|
183
|
+
height: 8px;
|
|
184
|
+
border-radius: 50%;
|
|
185
|
+
background: #2ed573;
|
|
186
|
+
animation: pulse 2s infinite;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@keyframes pulse {
|
|
190
|
+
0%, 100% { opacity: 1; }
|
|
191
|
+
50% { opacity: 0.5; }
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.toast {
|
|
195
|
+
position: fixed;
|
|
196
|
+
top: 20px;
|
|
197
|
+
right: 20px;
|
|
198
|
+
background: #2ed573;
|
|
199
|
+
color: white;
|
|
200
|
+
padding: 12px 24px;
|
|
201
|
+
border-radius: 8px;
|
|
202
|
+
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
|
203
|
+
transform: translateX(400px);
|
|
204
|
+
transition: transform 0.3s;
|
|
205
|
+
z-index: 1000;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.toast.show {
|
|
209
|
+
transform: translateX(0);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.help-text {
|
|
213
|
+
font-size: 12px;
|
|
214
|
+
color: #999;
|
|
215
|
+
margin-top: 8px;
|
|
216
|
+
line-height: 1.6;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.help-text code {
|
|
220
|
+
background: #f0f0f0;
|
|
221
|
+
padding: 2px 6px;
|
|
222
|
+
border-radius: 3px;
|
|
223
|
+
font-family: 'Courier New', monospace;
|
|
224
|
+
}
|
|
225
|
+
</style>
|
|
226
|
+
</head>
|
|
227
|
+
<body>
|
|
228
|
+
<div class="container">
|
|
229
|
+
<div class="header">
|
|
230
|
+
<h1>🔧 PasteKit Lab 域名配置</h1>
|
|
231
|
+
<p>配置需要拦截的域名,请求将通过 WebSocket 发送到 Chrome 插件</p>
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
<div class="content">
|
|
235
|
+
<div class="section">
|
|
236
|
+
<div class="section-title">添加域名</div>
|
|
237
|
+
<div class="domain-input-group">
|
|
238
|
+
<input
|
|
239
|
+
type="text"
|
|
240
|
+
id="domainInput"
|
|
241
|
+
class="domain-input"
|
|
242
|
+
placeholder="输入域名,如:example.com 或 *.test.com"
|
|
243
|
+
>
|
|
244
|
+
<button class="btn btn-primary" onclick="addDomain()">添加</button>
|
|
245
|
+
</div>
|
|
246
|
+
<div class="help-text">
|
|
247
|
+
💡 支持精确匹配(<code>example.com</code>)和通配符匹配(<code>*.example.com</code>)
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
<div class="section">
|
|
252
|
+
<div class="section-title">已配置的域名 (<span id="domainCount">0</span>)</div>
|
|
253
|
+
<ul id="domainList" class="domain-list">
|
|
254
|
+
<!-- 域名列表将在这里动态生成 -->
|
|
255
|
+
</ul>
|
|
256
|
+
<div id="emptyState" class="empty-state" style="display: none;">
|
|
257
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
258
|
+
<circle cx="12" cy="12" r="10"></circle>
|
|
259
|
+
<line x1="12" y1="8" x2="12" y2="12"></line>
|
|
260
|
+
<line x1="12" y1="16" x2="12.01" y2="16"></line>
|
|
261
|
+
</svg>
|
|
262
|
+
<p>暂无配置的域名</p>
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<div class="status-bar">
|
|
268
|
+
<div class="status-indicator">
|
|
269
|
+
<div class="status-dot"></div>
|
|
270
|
+
<span>WebSocket: ws://127.0.0.1:8889/ws</span>
|
|
271
|
+
</div>
|
|
272
|
+
<div id="connectionStatus">未连接</div>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<div id="toast" class="toast"></div>
|
|
277
|
+
|
|
278
|
+
<script>
|
|
279
|
+
let domains = [];
|
|
280
|
+
let ws = null;
|
|
281
|
+
let reconnectTimer = null;
|
|
282
|
+
|
|
283
|
+
// 初始化
|
|
284
|
+
function init() {
|
|
285
|
+
loadDomains();
|
|
286
|
+
connectWebSocket();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// 加载保存的域名
|
|
290
|
+
function loadDomains() {
|
|
291
|
+
const saved = localStorage.getItem('pastekitlab_domains');
|
|
292
|
+
if (saved) {
|
|
293
|
+
domains = JSON.parse(saved);
|
|
294
|
+
renderDomains();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// 保存域名
|
|
299
|
+
function saveDomains() {
|
|
300
|
+
localStorage.setItem('pastekitlab_domains', JSON.stringify(domains));
|
|
301
|
+
sendDomainConfig();
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// 渲染域名列表
|
|
305
|
+
function renderDomains() {
|
|
306
|
+
const listEl = document.getElementById('domainList');
|
|
307
|
+
const emptyEl = document.getElementById('emptyState');
|
|
308
|
+
const countEl = document.getElementById('domainCount');
|
|
309
|
+
|
|
310
|
+
countEl.textContent = domains.length;
|
|
311
|
+
|
|
312
|
+
if (domains.length === 0) {
|
|
313
|
+
listEl.innerHTML = '';
|
|
314
|
+
emptyEl.style.display = 'block';
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
emptyEl.style.display = 'none';
|
|
319
|
+
listEl.innerHTML = domains.map((domain, index) => `
|
|
320
|
+
<li class="domain-item">
|
|
321
|
+
<span class="domain-text">${escapeHtml(domain)}</span>
|
|
322
|
+
<button class="btn btn-danger" onclick="removeDomain(${index})">删除</button>
|
|
323
|
+
</li>
|
|
324
|
+
`).join('');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// 添加域名
|
|
328
|
+
function addDomain() {
|
|
329
|
+
const input = document.getElementById('domainInput');
|
|
330
|
+
const domain = input.value.trim();
|
|
331
|
+
|
|
332
|
+
if (!domain) {
|
|
333
|
+
showToast('请输入域名', 'error');
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// 验证域名格式
|
|
338
|
+
if (!isValidDomain(domain)) {
|
|
339
|
+
showToast('域名格式不正确', 'error');
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// 检查是否已存在
|
|
344
|
+
if (domains.includes(domain)) {
|
|
345
|
+
showToast('该域名已存在', 'error');
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
domains.push(domain);
|
|
350
|
+
saveDomains();
|
|
351
|
+
renderDomains();
|
|
352
|
+
input.value = '';
|
|
353
|
+
showToast('域名添加成功');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// 删除域名
|
|
357
|
+
function removeDomain(index) {
|
|
358
|
+
domains.splice(index, 1);
|
|
359
|
+
saveDomains();
|
|
360
|
+
renderDomains();
|
|
361
|
+
showToast('域名已删除');
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// 验证域名格式
|
|
365
|
+
function isValidDomain(domain) {
|
|
366
|
+
// 支持 *.example.com 或 example.com
|
|
367
|
+
const pattern = /^(\*\.)?([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
|
|
368
|
+
return pattern.test(domain);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// 连接 WebSocket
|
|
372
|
+
function connectWebSocket() {
|
|
373
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
try {
|
|
378
|
+
ws = new WebSocket('ws://127.0.0.1:8889/ws');
|
|
379
|
+
|
|
380
|
+
ws.onopen = () => {
|
|
381
|
+
console.log('WebSocket 连接成功');
|
|
382
|
+
updateConnectionStatus('已连接', true);
|
|
383
|
+
sendDomainConfig();
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
ws.onmessage = (event) => {
|
|
387
|
+
const message = JSON.parse(event.data);
|
|
388
|
+
console.log('收到消息:', message);
|
|
389
|
+
|
|
390
|
+
if (message.msgType === 'domainConfigReceived') {
|
|
391
|
+
showToast(`配置已同步 (${message.domainCount} 个域名)`);
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
ws.onclose = () => {
|
|
396
|
+
console.log('WebSocket 连接关闭');
|
|
397
|
+
updateConnectionStatus('未连接', false);
|
|
398
|
+
|
|
399
|
+
// 自动重连
|
|
400
|
+
if (!reconnectTimer) {
|
|
401
|
+
reconnectTimer = setTimeout(() => {
|
|
402
|
+
reconnectTimer = null;
|
|
403
|
+
connectWebSocket();
|
|
404
|
+
}, 3000);
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
ws.onerror = (error) => {
|
|
409
|
+
console.error('WebSocket 错误:', error);
|
|
410
|
+
updateConnectionStatus('连接错误', false);
|
|
411
|
+
};
|
|
412
|
+
} catch (error) {
|
|
413
|
+
console.error('连接失败:', error);
|
|
414
|
+
updateConnectionStatus('连接失败', false);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// 发送域名配置
|
|
419
|
+
function sendDomainConfig() {
|
|
420
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
421
|
+
const config = {
|
|
422
|
+
msgType: 'domain',
|
|
423
|
+
domain: domains,
|
|
424
|
+
mockUrls: []
|
|
425
|
+
};
|
|
426
|
+
ws.send(JSON.stringify(config));
|
|
427
|
+
console.log('已发送域名配置:', config);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// 更新连接状态
|
|
432
|
+
function updateConnectionStatus(text, connected) {
|
|
433
|
+
const statusEl = document.getElementById('connectionStatus');
|
|
434
|
+
statusEl.textContent = text;
|
|
435
|
+
statusEl.style.color = connected ? '#2ed573' : '#ff4757';
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// 显示提示
|
|
439
|
+
function showToast(message, type = 'success') {
|
|
440
|
+
const toast = document.getElementById('toast');
|
|
441
|
+
toast.textContent = message;
|
|
442
|
+
toast.style.background = type === 'error' ? '#ff4757' : '#2ed573';
|
|
443
|
+
toast.classList.add('show');
|
|
444
|
+
|
|
445
|
+
setTimeout(() => {
|
|
446
|
+
toast.classList.remove('show');
|
|
447
|
+
}, 3000);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// HTML 转义
|
|
451
|
+
function escapeHtml(text) {
|
|
452
|
+
const div = document.createElement('div');
|
|
453
|
+
div.textContent = text;
|
|
454
|
+
return div.innerHTML;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// 回车键添加域名
|
|
458
|
+
document.getElementById('domainInput').addEventListener('keypress', (e) => {
|
|
459
|
+
if (e.key === 'Enter') {
|
|
460
|
+
addDomain();
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
// 页面加载时初始化
|
|
465
|
+
window.addEventListener('DOMContentLoaded', init);
|
|
466
|
+
</script>
|
|
467
|
+
</body>
|
|
468
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "whistle.pastekitlab",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
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": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"whistleConfig": {
|
|
24
24
|
"name": "pastekitlab",
|
|
25
|
-
"description": "PasteKit Lab 请求拦截插件,通过 WebSocket 将请求发送到 requestlistviewer 展示"
|
|
25
|
+
"description": "PasteKit Lab 请求拦截插件,通过 WebSocket 将请求发送到 requestlistviewer 展示",
|
|
26
|
+
"optionsPage": "http://127.0.0.1:8890"
|
|
26
27
|
}
|
|
27
28
|
}
|
package/rules.txt
ADDED
package/config-whistle.sh
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# 配置 whistle 规则以使用 PasteKit Lab 插件
|
|
4
|
-
|
|
5
|
-
echo "🔧 配置 Whistle 规则..."
|
|
6
|
-
|
|
7
|
-
# 添加规则
|
|
8
|
-
w2 rule add "http://*.* pastekitlab" 2>/dev/null
|
|
9
|
-
w2 rule add "https://*.* pastekitlab" 2>/dev/null
|
|
10
|
-
|
|
11
|
-
echo "✅ 规则已添加"
|
|
12
|
-
echo ""
|
|
13
|
-
echo "📋 当前规则:"
|
|
14
|
-
w2 rule list | grep pastekitlab || echo "(无规则)"
|
|
15
|
-
echo ""
|
|
16
|
-
echo "💡 提示:"
|
|
17
|
-
echo " 1. 重启 whistle: w2 restart"
|
|
18
|
-
echo " 2. 访问 http://127.0.0.1:8899 查看规则"
|
|
19
|
-
echo " 3. Dashboard: http://127.0.0.1:8890"
|