cdp-tunnel 3.4.0 → 3.6.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.
- package/extension-new/cdp/handler/special.js +12 -2
- package/extension-new/config-page-preview.html +5 -0
- package/extension-new/config-page.js +15 -4
- package/extension-new/core/connection-state.js +9 -0
- package/extension-new/core/websocket.js +8 -3
- package/extension-new/manifest.json +1 -1
- package/extension-new/utils/config.js +1 -1
- package/extension-new/utils/helpers.js +9 -3
- package/package.json +1 -1
- package/server/admin-console.html +481 -0
- package/server/modules/config.js +5 -0
- package/server/modules/port-pool.js +144 -21
- package/server/proxy-server.js +323 -4
- package/server/saas/auth.js +1 -1
- package/server/saas/key-manager.js +98 -0
|
@@ -20,6 +20,15 @@ var SpecialHandler = (function() {
|
|
|
20
20
|
return (wm && wm.config && wm.config.tag) || null;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// 从 state 读取 key 名称(用作分组名),doGroup 等分组操作调用
|
|
24
|
+
function _getGroupName(clientId, context) {
|
|
25
|
+
var state = context ? context._state : null;
|
|
26
|
+
if (state && clientId && state.getGroupNameForClient) {
|
|
27
|
+
return state.getGroupNameForClient(clientId);
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
23
32
|
function muteTabIfNeeded(tabId) {
|
|
24
33
|
Config.getAutoMute(function(enabled) {
|
|
25
34
|
if (!enabled) return;
|
|
@@ -297,7 +306,7 @@ var SpecialHandler = (function() {
|
|
|
297
306
|
return;
|
|
298
307
|
}
|
|
299
308
|
}
|
|
300
|
-
var baseName = CDPUtils.getGroupBaseName(groupClientId, _getConnectionTag(context), context ? context.mode : null);
|
|
309
|
+
var baseName = CDPUtils.getGroupBaseName(groupClientId, _getConnectionTag(context), context ? context.mode : null, _getGroupName(groupClientId, context));
|
|
301
310
|
|
|
302
311
|
Logger.info('[TabGroup] Grouping tab immediately for:', baseName);
|
|
303
312
|
doGroup(tabId, groupClientId, baseName, 0, callback, context);
|
|
@@ -465,7 +474,8 @@ var SpecialHandler = (function() {
|
|
|
465
474
|
chrome.tabs.query({ groupId: groupId }, function(tabs) {
|
|
466
475
|
if (chrome.runtime.lastError || !tabs) return;
|
|
467
476
|
|
|
468
|
-
var
|
|
477
|
+
var groupName = (state && state.getGroupNameForClient) ? state.getGroupNameForClient(clientId) : null;
|
|
478
|
+
var baseName = CDPUtils.getGroupBaseName(clientId, connectionTag, mode, groupName);
|
|
469
479
|
var newName = baseName + ' (' + tabs.length + ')';
|
|
470
480
|
|
|
471
481
|
if (chrome.runtime.lastError || tabs.length === 0) return;
|
|
@@ -103,14 +103,16 @@
|
|
|
103
103
|
var isActive = conn.enabled && statusClass === 'connected';
|
|
104
104
|
var cdpAddr = getCdpAddress(conn.url, conn.mode);
|
|
105
105
|
var statusHtml = getStatusHtml(statusClass);
|
|
106
|
+
var hasKey = (conn.url || '').indexOf('key=') >= 0;
|
|
107
|
+
var maskedUrl = maskUrl(conn.url);
|
|
106
108
|
html +=
|
|
107
109
|
'<div class="conn-config-item' + (isActive ? ' active' : '') + '" data-id="' + conn.id + '">' +
|
|
108
110
|
'<input type="checkbox" class="conn-toggle" data-id="' + conn.id + '"' + (conn.enabled ? ' checked' : '') + ' title="启用/禁用">' +
|
|
109
111
|
'<span class="status-dot ' + statusClass + '" title="' + statusClass + '"></span>' +
|
|
110
112
|
'<div class="conn-config-info">' +
|
|
111
|
-
'<div class="conn-config-tag">' + (conn.mode === 'takeover' ? '🔗 ' : '🆕 ') + escapeHtml(conn.tag) + '</div>' +
|
|
112
|
-
'<div class="conn-config-url" title="' + escapeAttr(
|
|
113
|
-
(cdpAddr ? '<div class="conn-config-cdp">CDP: <span class="cdp-addr" data-cdp="' + escapeAttr(cdpAddr) + '">' + escapeHtml(cdpAddr) + '</span> <button class="btn-copy-cdp" data-cdp="' + escapeAttr(cdpAddr) + '" title="复制 CDP
|
|
113
|
+
'<div class="conn-config-tag">' + (conn.mode === 'takeover' ? '🔗 ' : '🆕 ') + escapeHtml(conn.tag) + (hasKey ? ' <span class="auth-badge" title="带 API Key 鉴权">🔑</span>' : '') + '</div>' +
|
|
114
|
+
'<div class="conn-config-url" title="' + escapeAttr(maskedUrl) + '">WS: ' + escapeHtml(maskedUrl) + '</div>' +
|
|
115
|
+
(cdpAddr ? '<div class="conn-config-cdp">CDP: <span class="cdp-addr" data-cdp="' + escapeAttr(cdpAddr) + '">' + escapeHtml(cdpAddr.replace(/(key=)(cdp_[a-f0-9]{8})[a-f0-9]*/, '$1$2••••')) + '</span> <button class="btn-copy-cdp" data-cdp="' + escapeAttr(cdpAddr) + '" title="复制 CDP 地址(含 key)">📋</button></div>' : '') +
|
|
114
116
|
'<div class="conn-config-status">' + statusHtml + '</div>' +
|
|
115
117
|
'</div>' +
|
|
116
118
|
'<button class="btn-delete" data-id="' + conn.id + '" title="删除">删除</button>' +
|
|
@@ -313,7 +315,16 @@
|
|
|
313
315
|
var host = match[1];
|
|
314
316
|
var port = parseInt(match[2], 10);
|
|
315
317
|
if (mode === 'takeover') port += 1;
|
|
316
|
-
|
|
318
|
+
// 保留 key(如果 URL 里带了 ?key=xxx,CDP 客户端连接也需要带)
|
|
319
|
+
var keyMatch = (wsUrl || '').match(/[?&]key=([^&]+)/);
|
|
320
|
+
var keyParam = keyMatch ? '?key=' + keyMatch[1] : '';
|
|
321
|
+
return 'http://' + host + ':' + port + keyParam;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// 显示 URL 时隐藏 key 的明文(防截图泄露),但保留可识别性
|
|
325
|
+
function maskUrl(wsUrl) {
|
|
326
|
+
if (!wsUrl) return '';
|
|
327
|
+
return wsUrl.replace(/([?&]key=)(cdp_[a-f0-9]{8})[a-f0-9]*/, '$1$2••••');
|
|
317
328
|
}
|
|
318
329
|
|
|
319
330
|
function init() {
|
|
@@ -308,6 +308,15 @@ ConnectionState.prototype.getTagForClient = function(clientId) {
|
|
|
308
308
|
return this.clientIdToTag.get(clientId) || null;
|
|
309
309
|
};
|
|
310
310
|
|
|
311
|
+
ConnectionState.prototype.setGroupNameForClient = function(clientId, groupName) {
|
|
312
|
+
if (!this.clientIdToGroupName) this.clientIdToGroupName = new Map();
|
|
313
|
+
if (clientId && groupName) this.clientIdToGroupName.set(clientId, groupName);
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
ConnectionState.prototype.getGroupNameForClient = function(clientId) {
|
|
317
|
+
return (this.clientIdToGroupName && this.clientIdToGroupName.get(clientId)) || null;
|
|
318
|
+
};
|
|
319
|
+
|
|
311
320
|
ConnectionState.prototype.addCDPClient = function(clientId, info) {
|
|
312
321
|
var exists = false;
|
|
313
322
|
for (var i = 0; i < this.cdpClients.length; i++) {
|
|
@@ -244,7 +244,12 @@ var WebSocketConnection = (function() {
|
|
|
244
244
|
if (message.__connectionTag) {
|
|
245
245
|
self.state.setTagForClient(message.clientId, message.__connectionTag);
|
|
246
246
|
}
|
|
247
|
-
|
|
247
|
+
// 存 groupName,供 doGroup 等后续分组操作使用
|
|
248
|
+
if (message.__groupName) {
|
|
249
|
+
self.state.setGroupNameForClient(message.clientId, message.__groupName);
|
|
250
|
+
}
|
|
251
|
+
// __groupName 来自 API Key 名称,用作 Chrome 分组名(一眼看出是谁的浏览器)
|
|
252
|
+
self._createGroupForClient(message.clientId, message.__mode, message.__groupName);
|
|
248
253
|
self._broadcastStateUpdate();
|
|
249
254
|
break;
|
|
250
255
|
|
|
@@ -555,7 +560,7 @@ var WebSocketConnection = (function() {
|
|
|
555
560
|
});
|
|
556
561
|
};
|
|
557
562
|
|
|
558
|
-
WebSocketConnection.prototype._createGroupForClient = function(clientId, mode) {
|
|
563
|
+
WebSocketConnection.prototype._createGroupForClient = function(clientId, mode, groupName) {
|
|
559
564
|
var self = this;
|
|
560
565
|
if (!clientId || !chrome.tabGroups) return;
|
|
561
566
|
if (mode === 'takeover') {
|
|
@@ -581,7 +586,7 @@ var WebSocketConnection = (function() {
|
|
|
581
586
|
self.state.setGroupCreationPromise(clientId, readyPromise);
|
|
582
587
|
|
|
583
588
|
var tag = (self.state.getTagForClient ? self.state.getTagForClient(clientId) : null) || (self.config ? self.config.tag : null);
|
|
584
|
-
var baseName = CDPUtils.getGroupBaseName(clientId, tag, mode);
|
|
589
|
+
var baseName = CDPUtils.getGroupBaseName(clientId, tag, mode, groupName);
|
|
585
590
|
chrome.tabs.query({ currentWindow: true }, function(tabs) {
|
|
586
591
|
if (!tabs || tabs.length === 0) {
|
|
587
592
|
Logger.warn('[WS:' + self.connectionId + '] No tabs found for group creation');
|
|
@@ -42,8 +42,14 @@ var CDPUtils = (function() {
|
|
|
42
42
|
return 0;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
function buildGroupName(clientId, connectionTag, mode) {
|
|
45
|
+
function buildGroupName(clientId, connectionTag, mode, groupName) {
|
|
46
46
|
if (!clientId) return 'CDP';
|
|
47
|
+
// 如果传了 groupName(来自 API Key 的名称),直接用它作为分组名
|
|
48
|
+
// Chrome 分组显示为 "CDP-张三的浏览器",一眼看出是谁的浏览器
|
|
49
|
+
if (groupName) {
|
|
50
|
+
var prefixG = (mode === 'takeover') ? 'TAKE-' : 'CDP-';
|
|
51
|
+
return prefixG + groupName;
|
|
52
|
+
}
|
|
47
53
|
var hash = 0;
|
|
48
54
|
for (var i = 0; i < clientId.length; i++) {
|
|
49
55
|
var chr = clientId.charCodeAt(i);
|
|
@@ -56,8 +62,8 @@ var CDPUtils = (function() {
|
|
|
56
62
|
return prefix + tag + suffix;
|
|
57
63
|
}
|
|
58
64
|
|
|
59
|
-
function getGroupBaseName(clientId, connectionTag, mode) {
|
|
60
|
-
return buildGroupName(clientId, connectionTag, mode);
|
|
65
|
+
function getGroupBaseName(clientId, connectionTag, mode, groupName) {
|
|
66
|
+
return buildGroupName(clientId, connectionTag, mode, groupName);
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
function findGroupByName(allGroups, baseName) {
|
package/package.json
CHANGED