@feng3d/ctc 0.0.19 → 0.1.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/README.md +82 -106
- package/dist/cli.d.ts +13 -11
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +188 -1067
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +18 -55
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +42 -188
- package/dist/config.js.map +1 -1
- package/dist/controller.d.ts +47 -5
- package/dist/controller.d.ts.map +1 -1
- package/dist/controller.js +10 -15
- package/dist/controller.js.map +1 -1
- package/dist/handlers/unified-handler.d.ts.map +1 -1
- package/dist/handlers/unified-handler.js +3 -1
- package/dist/handlers/unified-handler.js.map +1 -1
- package/dist/index.d.ts +20 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -328
- package/dist/index.js.map +1 -1
- package/dist/proxy-manager.d.ts +32 -32
- package/dist/proxy-manager.d.ts.map +1 -1
- package/dist/proxy-manager.js +81 -66
- package/dist/proxy-manager.js.map +1 -1
- package/package.json +5 -6
- package/admin-ui/dist/app.js +0 -29
- package/admin-ui/dist/index.html +0 -222
- package/admin-ui/dist/style.css +0 -1
- package/dist/admin-server.d.ts +0 -143
- package/dist/admin-server.d.ts.map +0 -1
- package/dist/admin-server.js +0 -407
- package/dist/admin-server.js.map +0 -1
- package/dist/forward-proxy.d.ts +0 -100
- package/dist/forward-proxy.d.ts.map +0 -1
- package/dist/forward-proxy.js +0 -407
- package/dist/forward-proxy.js.map +0 -1
- package/dist/ipc-handler.d.ts +0 -52
- package/dist/ipc-handler.d.ts.map +0 -1
- package/dist/ipc-handler.js +0 -110
- package/dist/ipc-handler.js.map +0 -1
package/dist/proxy-manager.d.ts
CHANGED
|
@@ -3,79 +3,79 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 代理管理器模块。
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* 创建统一的处理器(同时支持 HTTP
|
|
8
|
-
* 以及在连接断开时清理所有处理器。
|
|
6
|
+
* 负责管理代理隧道的生命周期:向服务器注册代理(远程端口由服务端自动分配)、
|
|
7
|
+
* 创建统一的处理器(同时支持 HTTP/WebSocket/TCP/UDP)、断连时清理处理器。
|
|
9
8
|
*/
|
|
10
9
|
import { EventEmitter } from 'events';
|
|
11
10
|
import { Controller } from './controller.js';
|
|
12
11
|
import { ProxyConfig } from '@feng3d/chuantou-shared';
|
|
13
12
|
/**
|
|
14
|
-
*
|
|
13
|
+
* 代理管理器类,负责管理代理隧道的注册、注销和生命周期。
|
|
15
14
|
*
|
|
16
|
-
* 使用 UnifiedHandler 处理所有协议(HTTP、WebSocket、TCP)。
|
|
15
|
+
* 使用 UnifiedHandler 处理所有协议(HTTP、WebSocket、TCP、UDP)。
|
|
17
16
|
* 当控制器连接断开时,自动清理所有处理器。
|
|
18
17
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* await proxyManager.registerProxy({ remotePort: 8080, localPort: 3000 });
|
|
23
|
-
* await proxyManager.registerProxy({ remotePort: 2222, localPort: 22 });
|
|
24
|
-
* ```
|
|
18
|
+
* 事件:
|
|
19
|
+
* - `registered` — 代理注册成功,参数为服务端实际分配的远程端口号
|
|
20
|
+
* - `handlerCreated` — 处理器已创建
|
|
25
21
|
*/
|
|
26
22
|
export declare class ProxyManager extends EventEmitter {
|
|
27
23
|
/** 控制器实例,用于与服务器通信 */
|
|
28
24
|
private controller;
|
|
29
|
-
/**
|
|
25
|
+
/** 代理处理器映射表,键为服务端分配的远程端口号,值为对应的处理器实例 */
|
|
30
26
|
private handlers;
|
|
27
|
+
/** 待注册的代理配置(连接建立后批量注册) */
|
|
28
|
+
private pending;
|
|
29
|
+
constructor(controller: Controller);
|
|
31
30
|
/**
|
|
32
|
-
*
|
|
31
|
+
* 添加待注册的代理配置(连接建立后自动注册)。
|
|
33
32
|
*
|
|
34
|
-
* @param
|
|
33
|
+
* @param config - 代理配置对象,包含本地端口等信息
|
|
35
34
|
*/
|
|
36
|
-
|
|
35
|
+
addPending(config: ProxyConfig): void;
|
|
37
36
|
/**
|
|
38
37
|
* 向服务器注册一个代理隧道。
|
|
39
38
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
39
|
+
* 发送注册消息到服务器(不指定远程端口,由服务端自动分配),
|
|
40
|
+
* 注册成功后用服务端返回的实际端口创建 UnifiedHandler。
|
|
42
41
|
*
|
|
43
|
-
* @param config -
|
|
42
|
+
* @param config - 代理配置对象,包含本地端口等信息
|
|
43
|
+
* @returns 服务端实际分配的远程端口号;未连接时返回 null
|
|
44
44
|
* @throws {Error} 注册失败时抛出错误,包含服务器返回的错误信息
|
|
45
45
|
*/
|
|
46
|
-
registerProxy(config: ProxyConfig): Promise<
|
|
46
|
+
registerProxy(config: ProxyConfig): Promise<number | null>;
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
* 实际执行注册流程(已认证时调用)
|
|
49
|
+
*/
|
|
50
|
+
private doRegister;
|
|
51
|
+
/**
|
|
52
|
+
* 注册所有待注册的代理。
|
|
51
53
|
*
|
|
52
|
-
*
|
|
54
|
+
* 在控制器认证成功后调用。
|
|
53
55
|
*/
|
|
54
|
-
|
|
56
|
+
registerPending(): Promise<void>;
|
|
55
57
|
/**
|
|
56
|
-
*
|
|
58
|
+
* 注销指定远程端口的代理隧道。
|
|
57
59
|
*
|
|
58
|
-
*
|
|
60
|
+
* 销毁对应的处理器,并向服务器发送注销请求(仅在已连接时)。
|
|
59
61
|
*
|
|
60
|
-
* @
|
|
62
|
+
* @param remotePort - 要注销的代理对应的服务端远程端口号
|
|
61
63
|
*/
|
|
62
|
-
|
|
64
|
+
unregisterProxy(remotePort: number): Promise<void>;
|
|
63
65
|
/**
|
|
64
66
|
* 处理控制器连接断开事件。
|
|
65
67
|
*
|
|
66
|
-
*
|
|
68
|
+
* 销毁所有处理器并清空处理器映射表(保留 pending 列表以便重连后重新注册)。
|
|
67
69
|
*/
|
|
68
70
|
private onDisconnected;
|
|
69
71
|
/**
|
|
70
|
-
*
|
|
72
|
+
* 获取所有已注册的服务端远程端口列表
|
|
71
73
|
*
|
|
72
74
|
* @returns 已注册的远程端口号数组
|
|
73
75
|
*/
|
|
74
76
|
getRegisteredPorts(): number[];
|
|
75
77
|
/**
|
|
76
78
|
* 销毁代理管理器,注销所有代理并释放资源。
|
|
77
|
-
*
|
|
78
|
-
* @returns 销毁完成后解析的 Promise
|
|
79
79
|
*/
|
|
80
80
|
destroy(): Promise<void>;
|
|
81
81
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy-manager.d.ts","sourceRoot":"","sources":["../src/proxy-manager.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"proxy-manager.d.ts","sourceRoot":"","sources":["../src/proxy-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAU,MAAM,yBAAyB,CAAC;AAW9D;;;;;;;;;GASG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,qBAAqB;IACrB,OAAO,CAAC,UAAU,CAAa;IAE/B,wCAAwC;IACxC,OAAO,CAAC,QAAQ,CAA2B;IAE3C,0BAA0B;IAC1B,OAAO,CAAC,OAAO,CAAqB;gBAExB,UAAU,EAAE,UAAU;IAWlC;;;;OAIG;IACH,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAIrC;;;;;;;;;OASG;IACG,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAahE;;OAEG;YACW,UAAU;IAqCxB;;;;OAIG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAatC;;;;;;OAMG;IACG,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBxD;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAQtB;;;;OAIG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAI9B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAI/B"}
|
package/dist/proxy-manager.js
CHANGED
|
@@ -3,35 +3,28 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 代理管理器模块。
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* 创建统一的处理器(同时支持 HTTP
|
|
8
|
-
* 以及在连接断开时清理所有处理器。
|
|
6
|
+
* 负责管理代理隧道的生命周期:向服务器注册代理(远程端口由服务端自动分配)、
|
|
7
|
+
* 创建统一的处理器(同时支持 HTTP/WebSocket/TCP/UDP)、断连时清理处理器。
|
|
9
8
|
*/
|
|
10
9
|
import { EventEmitter } from 'events';
|
|
11
10
|
import { logger } from '@feng3d/chuantou-shared';
|
|
12
11
|
import { UnifiedHandler } from './handlers/unified-handler.js';
|
|
13
12
|
import { MessageType, createMessage } from '@feng3d/chuantou-shared';
|
|
14
13
|
/**
|
|
15
|
-
*
|
|
14
|
+
* 代理管理器类,负责管理代理隧道的注册、注销和生命周期。
|
|
16
15
|
*
|
|
17
|
-
* 使用 UnifiedHandler 处理所有协议(HTTP、WebSocket、TCP)。
|
|
16
|
+
* 使用 UnifiedHandler 处理所有协议(HTTP、WebSocket、TCP、UDP)。
|
|
18
17
|
* 当控制器连接断开时,自动清理所有处理器。
|
|
19
18
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* await proxyManager.registerProxy({ remotePort: 8080, localPort: 3000 });
|
|
24
|
-
* await proxyManager.registerProxy({ remotePort: 2222, localPort: 22 });
|
|
25
|
-
* ```
|
|
19
|
+
* 事件:
|
|
20
|
+
* - `registered` — 代理注册成功,参数为服务端实际分配的远程端口号
|
|
21
|
+
* - `handlerCreated` — 处理器已创建
|
|
26
22
|
*/
|
|
27
23
|
export class ProxyManager extends EventEmitter {
|
|
28
|
-
/**
|
|
29
|
-
* 创建代理管理器实例。
|
|
30
|
-
*
|
|
31
|
-
* @param controller - 控制器实例,用于与服务器进行通信
|
|
32
|
-
*/
|
|
33
24
|
constructor(controller) {
|
|
34
25
|
super();
|
|
26
|
+
/** 待注册的代理配置(连接建立后批量注册) */
|
|
27
|
+
this.pending = [];
|
|
35
28
|
this.controller = controller;
|
|
36
29
|
this.handlers = new Map();
|
|
37
30
|
// 监听控制器事件
|
|
@@ -39,54 +32,92 @@ export class ProxyManager extends EventEmitter {
|
|
|
39
32
|
this.onDisconnected();
|
|
40
33
|
});
|
|
41
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* 添加待注册的代理配置(连接建立后自动注册)。
|
|
37
|
+
*
|
|
38
|
+
* @param config - 代理配置对象,包含本地端口等信息
|
|
39
|
+
*/
|
|
40
|
+
addPending(config) {
|
|
41
|
+
this.pending.push(config);
|
|
42
|
+
}
|
|
42
43
|
/**
|
|
43
44
|
* 向服务器注册一个代理隧道。
|
|
44
45
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
46
|
+
* 发送注册消息到服务器(不指定远程端口,由服务端自动分配),
|
|
47
|
+
* 注册成功后用服务端返回的实际端口创建 UnifiedHandler。
|
|
47
48
|
*
|
|
48
|
-
* @param config -
|
|
49
|
+
* @param config - 代理配置对象,包含本地端口等信息
|
|
50
|
+
* @returns 服务端实际分配的远程端口号;未连接时返回 null
|
|
49
51
|
* @throws {Error} 注册失败时抛出错误,包含服务器返回的错误信息
|
|
50
52
|
*/
|
|
51
53
|
async registerProxy(config) {
|
|
52
|
-
logger.log(
|
|
53
|
-
//
|
|
54
|
-
if (this.controller.isAuthenticated()) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
logger.log(`正在注册代理 -> ${config.localHost || 'localhost'}:${config.localPort}`);
|
|
55
|
+
// 未认证时仅记录待注册列表
|
|
56
|
+
if (!this.controller.isAuthenticated()) {
|
|
57
|
+
this.addPending(config);
|
|
58
|
+
logger.log(`代理已加入待注册列表 -> ${config.localHost || 'localhost'}:${config.localPort} (待连接后自动注册)`);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return this.doRegister(config);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 实际执行注册流程(已认证时调用)
|
|
65
|
+
*/
|
|
66
|
+
async doRegister(config) {
|
|
67
|
+
const registerMsg = createMessage(MessageType.REGISTER, {
|
|
68
|
+
remotePort: config.remotePort,
|
|
69
|
+
localPort: config.localPort,
|
|
70
|
+
localHost: config.localHost,
|
|
71
|
+
});
|
|
72
|
+
let response;
|
|
73
|
+
try {
|
|
74
|
+
response = await this.controller.sendRequest(registerMsg);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
78
|
+
throw new Error(`注册代理失败: ${errorMessage}`);
|
|
79
|
+
}
|
|
80
|
+
if (!response.payload.success || response.payload.remotePort === undefined) {
|
|
81
|
+
throw new Error(`注册代理失败: ${response.payload.error}`);
|
|
82
|
+
}
|
|
83
|
+
// 使用服务端实际分配的远程端口
|
|
84
|
+
const assignedPort = response.payload.remotePort;
|
|
85
|
+
logger.log(`代理已注册: ${response.payload.remoteUrl || assignedPort}`);
|
|
86
|
+
// 用实际分配端口创建处理器
|
|
87
|
+
const handlerConfig = { ...config, remotePort: assignedPort };
|
|
88
|
+
const handler = new UnifiedHandler(this.controller, handlerConfig);
|
|
89
|
+
handler.on('error', (error) => {
|
|
90
|
+
logger.error(`端口 ${assignedPort} 的处理器错误:`, error.message);
|
|
91
|
+
});
|
|
92
|
+
this.handlers.set(assignedPort, handler);
|
|
93
|
+
this.emit('registered', assignedPort);
|
|
94
|
+
this.emit('handlerCreated', assignedPort);
|
|
95
|
+
return assignedPort;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 注册所有待注册的代理。
|
|
99
|
+
*
|
|
100
|
+
* 在控制器认证成功后调用。
|
|
101
|
+
*/
|
|
102
|
+
async registerPending() {
|
|
103
|
+
const pending = [...this.pending];
|
|
104
|
+
this.pending = [];
|
|
105
|
+
for (const config of pending) {
|
|
60
106
|
try {
|
|
61
|
-
|
|
62
|
-
if (!response.payload.success) {
|
|
63
|
-
throw new Error(`注册代理失败: ${response.payload.error}`);
|
|
64
|
-
}
|
|
65
|
-
logger.log(`代理已注册: ${response.payload.remoteUrl}`);
|
|
107
|
+
await this.doRegister(config);
|
|
66
108
|
}
|
|
67
109
|
catch (error) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
throw new Error(`注册代理失败: ${errorMessage}`);
|
|
110
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
111
|
+
logger.error(`注册代理失败 -> ${config.localHost || 'localhost'}:${config.localPort}: ${msg}`);
|
|
71
112
|
}
|
|
72
113
|
}
|
|
73
|
-
else {
|
|
74
|
-
logger.log(`代理已添加到本地列表: :${config.remotePort} -> ${config.localHost || 'localhost'}:${config.localPort} (待连接后自动注册)`);
|
|
75
|
-
}
|
|
76
|
-
// 无论是否连接成功,都创建本地处理器
|
|
77
|
-
const handler = new UnifiedHandler(this.controller, config);
|
|
78
|
-
handler.on('error', (error) => {
|
|
79
|
-
logger.error(`端口 ${config.remotePort} 的处理器错误:`, error);
|
|
80
|
-
});
|
|
81
|
-
this.handlers.set(config.remotePort, handler);
|
|
82
|
-
this.emit('handlerCreated', config.remotePort);
|
|
83
114
|
}
|
|
84
115
|
/**
|
|
85
116
|
* 注销指定远程端口的代理隧道。
|
|
86
117
|
*
|
|
87
118
|
* 销毁对应的处理器,并向服务器发送注销请求(仅在已连接时)。
|
|
88
119
|
*
|
|
89
|
-
* @param remotePort -
|
|
120
|
+
* @param remotePort - 要注销的代理对应的服务端远程端口号
|
|
90
121
|
*/
|
|
91
122
|
async unregisterProxy(remotePort) {
|
|
92
123
|
const handler = this.handlers.get(remotePort);
|
|
@@ -104,7 +135,6 @@ export class ProxyManager extends EventEmitter {
|
|
|
104
135
|
logger.log(`代理已注销: 端口 ${remotePort}`);
|
|
105
136
|
}
|
|
106
137
|
catch (error) {
|
|
107
|
-
// 即使服务器注销失败,本地也已经删除了,记录错误但不抛出
|
|
108
138
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
109
139
|
logger.error(`服务器注销代理失败: ${errorMessage}`);
|
|
110
140
|
}
|
|
@@ -113,24 +143,10 @@ export class ProxyManager extends EventEmitter {
|
|
|
113
143
|
logger.log(`代理已移除: 端口 ${remotePort} (未连接到服务器)`);
|
|
114
144
|
}
|
|
115
145
|
}
|
|
116
|
-
/**
|
|
117
|
-
* 注销所有已注册的代理隧道。
|
|
118
|
-
*
|
|
119
|
-
* 并行注销所有代理,等待全部注销完成后返回。
|
|
120
|
-
*
|
|
121
|
-
* @returns 所有代理注销完成后解析的 Promise
|
|
122
|
-
*/
|
|
123
|
-
async unregisterAll() {
|
|
124
|
-
const unregisterPromises = [];
|
|
125
|
-
for (const port of this.handlers.keys()) {
|
|
126
|
-
unregisterPromises.push(this.unregisterProxy(port));
|
|
127
|
-
}
|
|
128
|
-
await Promise.all(unregisterPromises);
|
|
129
|
-
}
|
|
130
146
|
/**
|
|
131
147
|
* 处理控制器连接断开事件。
|
|
132
148
|
*
|
|
133
|
-
*
|
|
149
|
+
* 销毁所有处理器并清空处理器映射表(保留 pending 列表以便重连后重新注册)。
|
|
134
150
|
*/
|
|
135
151
|
onDisconnected() {
|
|
136
152
|
logger.log('连接丢失,正在停止所有处理器...');
|
|
@@ -140,7 +156,7 @@ export class ProxyManager extends EventEmitter {
|
|
|
140
156
|
this.handlers.clear();
|
|
141
157
|
}
|
|
142
158
|
/**
|
|
143
|
-
*
|
|
159
|
+
* 获取所有已注册的服务端远程端口列表
|
|
144
160
|
*
|
|
145
161
|
* @returns 已注册的远程端口号数组
|
|
146
162
|
*/
|
|
@@ -149,11 +165,10 @@ export class ProxyManager extends EventEmitter {
|
|
|
149
165
|
}
|
|
150
166
|
/**
|
|
151
167
|
* 销毁代理管理器,注销所有代理并释放资源。
|
|
152
|
-
*
|
|
153
|
-
* @returns 销毁完成后解析的 Promise
|
|
154
168
|
*/
|
|
155
169
|
async destroy() {
|
|
156
|
-
|
|
170
|
+
const ports = Array.from(this.handlers.keys());
|
|
171
|
+
await Promise.all(ports.map((port) => this.unregisterProxy(port)));
|
|
157
172
|
}
|
|
158
173
|
}
|
|
159
174
|
//# sourceMappingURL=proxy-manager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy-manager.js","sourceRoot":"","sources":["../src/proxy-manager.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"proxy-manager.js","sourceRoot":"","sources":["../src/proxy-manager.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@feng3d/ctc",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -14,12 +14,11 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
|
-
"dist"
|
|
18
|
-
"admin-ui/dist"
|
|
17
|
+
"dist"
|
|
19
18
|
],
|
|
20
19
|
"scripts": {
|
|
21
|
-
"
|
|
22
|
-
"build
|
|
20
|
+
"start": "node dist/cli.js",
|
|
21
|
+
"build": "tsc",
|
|
23
22
|
"watch": "tsc --watch --pretty",
|
|
24
23
|
"clean": "rm -rf dist",
|
|
25
24
|
"test": "vitest run",
|
|
@@ -27,7 +26,7 @@
|
|
|
27
26
|
"test:coverage": "vitest run --coverage"
|
|
28
27
|
},
|
|
29
28
|
"dependencies": {
|
|
30
|
-
"@feng3d/chuantou-shared": "^0.0
|
|
29
|
+
"@feng3d/chuantou-shared": "^0.1.0",
|
|
31
30
|
"chalk": "^5.6.2",
|
|
32
31
|
"commander": "^14.0.3",
|
|
33
32
|
"uuid": "^9.0.1",
|
package/admin-ui/dist/app.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))o(s);new MutationObserver(s=>{for(const r of s)if(r.type==="childList")for(const a of r.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&o(a)}).observe(document,{childList:!0,subtree:!0});function n(s){const r={};return s.integrity&&(r.integrity=s.integrity),s.referrerPolicy&&(r.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?r.credentials="include":s.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function o(s){if(s.ep)return;s.ep=!0;const r=n(s);fetch(s.href,r)}})();let p=null,h=null,E="reverse",I="";const m=new Map;let x=null;function S(t){const e=Math.floor(t/1e3),n=Math.floor(e/60),o=Math.floor(n/60),s=Math.floor(o/24);return s>0?`${s}天 ${o%24}小时`:o>0?`${o}小时 ${n%60}分钟`:n>0?`${n}分钟 ${e%60}秒`:`${e}秒`}function c(t,e="success"){const n=document.getElementById("toast");n.textContent=t,n.className=`toast ${e} show`,setTimeout(()=>n.classList.remove("show"),3e3)}async function w(){try{const n=(await(await fetch("/_ctc/status")).json()).proxies||[];if(n.length===0)return;for(const s of n)m.set(s.remotePort,"testing");v();const o=n.map(async s=>{const r=s.remotePort;try{const a=await fetch(`/_ctc/test-proxy?port=${r}`,{method:"POST"});a.ok&&(await a.json()).success?m.set(r,"connected"):m.set(r,"failed")}catch{m.set(r,"failed")}v()});await Promise.all(o)}catch(t){console.error("测试代理连接失败:",t)}}function j(t){var e,n,o,s,r,a;E=t,document.querySelectorAll(".tab").forEach(i=>i.classList.remove("active")),t==="reverse"?(e=document.getElementById("tabReverse"))==null||e.classList.add("active"):t==="forward"?(n=document.getElementById("tabForward"))==null||n.classList.add("active","forward-tab"):(o=document.getElementById("tabGuide"))==null||o.classList.add("active"),(s=document.getElementById("reversePanel"))==null||s.classList.toggle("hidden",t!=="reverse"),(r=document.getElementById("forwardPanel"))==null||r.classList.toggle("hidden",t!=="forward"),(a=document.getElementById("guidePanel"))==null||a.classList.toggle("hidden",t!=="guide"),t==="reverse"&&w()}function M(){x&&clearInterval(x),x=setInterval(()=>{E==="reverse"&&w()},1e4)}async function v(){try{const e=await(await fetch("/_ctc/status")).json(),n=document.getElementById("status");n&&(e.running?(n.textContent=e.authenticated?"已连接":e.connected?"认证中...":"连接中...",n.className="status running"):(n.textContent="已停止",n.className="status stopped"));const o=document.getElementById("server");o&&(o.textContent=e.serverUrl.replace("ws://","").replace("wss://",""));const s=document.getElementById("connection");s&&(s.textContent=e.authenticated?"已认证":e.connected?"已连接":"未连接");const r=document.getElementById("uptime");r&&(r.textContent=S(e.uptime));const a=document.getElementById("proxyCount");a&&(a.textContent=String(e.proxies.length));const i=document.getElementById("reconnectCount");i&&(i.textContent=String(e.reconnectAttempts));const d=document.getElementById("lastUpdate");d&&(d.textContent=new Date().toLocaleTimeString("zh-CN")),e.isRegistered!==void 0&&b(e.isRegistered);const u=document.getElementById("registerStatus");u&&e.clientId&&(u.textContent=`已注册 (ID: ${e.clientId})`);const f=new Set(e.proxies.map(l=>l.remotePort));for(const l of m.keys())f.has(l)||m.delete(l);const y=document.getElementById("proxiesList");y&&(e.proxies.length===0?y.innerHTML='<div class="empty-state">暂无反向代理映射,点击上方按钮添加</div>':y.innerHTML=e.proxies.map(l=>{const g=m.get(l.remotePort)||"unknown",B=g==="connected"?"status-connected":g==="failed"?"status-failed":g==="testing"?"status-testing":"status-unknown",T=g==="connected"?"✓":g==="failed"?"✗":g==="testing"?"⟳":"?";return`
|
|
2
|
-
<div class="proxy-item">
|
|
3
|
-
<div class="proxy-info">
|
|
4
|
-
<span class="proxy-index">#${l.index||"-"}</span>
|
|
5
|
-
<span class="proxy-protocol">ALL</span>
|
|
6
|
-
<span class="proxy-remote">:${l.remotePort}</span>
|
|
7
|
-
<span class="proxy-arrow">→</span>
|
|
8
|
-
<span class="proxy-local">${l.localHost||"localhost"}:${l.localPort}</span>
|
|
9
|
-
</div>
|
|
10
|
-
<div class="proxy-status ${B}" title="连接状态">${T}</div>
|
|
11
|
-
<button class="btn btn-danger" onclick="showDeleteModal(${l.remotePort}, 'reverse')">删除</button>
|
|
12
|
-
</div>
|
|
13
|
-
`}).join("")),e.forwardProxies&&$(e.forwardProxies)}catch(t){console.error("获取状态失败:",t)}}function b(t){const e=document.getElementById("registerStatus");e&&(t?(e.textContent="已注册",e.style.color="#00ff88"):(e.textContent="未注册",e.style.color="#888"))}function $(t){const e=document.getElementById("forwardCount");e&&(e.textContent=String(t.length));const n=document.getElementById("forwardList");n&&(t.length===0?n.innerHTML='<div class="empty-state">暂无正向穿透映射,点击上方按钮添加</div>':n.innerHTML=t.map(o=>`
|
|
14
|
-
<div class="proxy-item forward-item">
|
|
15
|
-
<div class="proxy-info">
|
|
16
|
-
<span class="proxy-index">→</span>
|
|
17
|
-
<span class="proxy-protocol">P2P</span>
|
|
18
|
-
<span class="proxy-remote">:${o.localPort}</span>
|
|
19
|
-
<span class="proxy-arrow">→</span>
|
|
20
|
-
<span class="proxy-local">${o.targetClientId}:${o.targetPort}</span>
|
|
21
|
-
</div>
|
|
22
|
-
<button class="btn btn-danger" onclick="showDeleteModal(${o.localPort}, 'forward')">删除</button>
|
|
23
|
-
</div>
|
|
24
|
-
`).join(""))}function k(t){const e=document.getElementById("clientsSection"),n=document.getElementById("clientsList"),o=document.getElementById("targetClientId");if(!t||t.length===0){e&&e.classList.add("hidden"),o&&(o.innerHTML='<option value="">无在线客户端</option>');return}if(e&&e.classList.remove("hidden"),n&&(n.innerHTML=t.map(s=>`
|
|
25
|
-
<div class="client-card ${s.id===I?"selected":""}" onclick="selectClient('${s.id}')">
|
|
26
|
-
<div class="client-id">${s.id}</div>
|
|
27
|
-
<div class="client-desc">${s.description||"无描述"}</div>
|
|
28
|
-
</div>
|
|
29
|
-
`).join("")),o){const s=o.value;o.innerHTML='<option value="">选择目标客户端</option>'+t.map(r=>`<option value="${r.id}">${r.id} (${r.description||"无描述"})</option>`).join(""),s&&(o.value=s)}}function O(t){if(I=t,document.querySelectorAll(".client-card").forEach(e=>e.classList.remove("selected")),event&&event.target){const e=event.target.closest(".client-card");e&&e.classList.add("selected")}}async function L(){try{const t=await fetch("/_ctc/forward/clients");if(t.ok){const e=await t.json();k(e.clients||[])}else{const e=await t.json();c(`获取客户端列表失败: ${e.error}`,"error")}}catch{c("获取客户端列表失败: 网络错误","error")}}async function D(){try{const e=await(await fetch("/_ctc/forward/register",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({description:"feng3d-ctc 客户端"})})).json();if(e.success){c("已注册到服务器");const n=document.getElementById("registerBtn");n&&(n.textContent="已注册",n.disabled=!0),L()}else c(`注册失败: ${e.error}`,"error")}catch{c("注册失败: 网络错误","error")}}function R(t,e="reverse"){var n;e==="reverse"?(p=t,h=null):(h=t,p=null),(n=document.getElementById("deleteModal"))==null||n.classList.add("show")}async function _(){const t=document.getElementById("reconnectBtn");if(!t)return;t.disabled=!0;const e=t.textContent;t.textContent="连接中...";try{const o=await(await fetch("/_ctc/reconnect",{method:"POST"})).json();o.success?(c(o.message||"正在重连...","success"),setTimeout(()=>v(),1e3)):c(`重连失败: ${o.error}`,"error")}catch{c("重连请求失败: 网络错误","error")}finally{t.disabled=!1,t.textContent=e}}async function H(){var t;if((t=document.getElementById("deleteModal"))==null||t.classList.remove("show"),p!==null)try{const e=await fetch(`/_ctc/proxies/${p}`,{method:"DELETE"});if(e.ok)c("反向代理已删除"),m.delete(p),v();else{const n=await e.json();c(`删除失败: ${n.error}`,"error")}}catch{c("删除失败: 网络错误","error")}if(h!==null)try{const n=await(await fetch("/_ctc/forward/remove",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({localPort:h})})).json();n.success?(c("正向穿透已删除"),P()):c(`删除失败: ${n.error}`,"error")}catch{c("删除失败: 网络错误","error")}p=null,h=null}function N(){const t=document.getElementById("showAddForm"),e=document.getElementById("cancelAdd"),n=document.getElementById("addProxy"),o=document.getElementById("addForm"),s=document.getElementById("testReverseProxy");t==null||t.addEventListener("click",()=>{o==null||o.classList.add("show")}),e==null||e.addEventListener("click",()=>{o==null||o.classList.remove("show")}),s==null||s.addEventListener("click",async()=>{try{const i=(await(await fetch("/_ctc/status")).json()).proxies||[];if(i.length===0){c("请先添加反向代理","error");return}c(`正在测试 ${i.length} 个反向代理...`),await w();const d=Array.from(m.values()).filter(f=>f==="connected").length,u=Array.from(m.values()).filter(f=>f==="failed").length;u===0?c(`测试完成: 全部成功 (${d}/${i.length})`):c(`测试完成: 成功 ${d}, 失败 ${u}`,"error")}catch(r){c(`测试失败: ${r.message}`,"error")}}),n==null||n.addEventListener("click",async()=>{const r=document.getElementById("newRemotePort"),a=document.getElementById("newLocalPort"),i=document.getElementById("newLocalHost"),d=parseInt(r.value),u=parseInt(a.value),f=i.value||"localhost";if(!d||!u){c("请填写完整信息","error");return}try{const y=await fetch("/_ctc/proxies",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({remotePort:d,localPort:u,localHost:f})});if(y.ok)c("反向代理已添加"),o==null||o.classList.remove("show"),r.value="",a.value="",i.value="localhost",await v(),await w();else{const l=await y.json();c(`添加失败: ${l.error}`,"error")}}catch{c("添加失败: 网络错误","error")}})}async function P(){try{const t=await fetch("/_ctc/forward/list");if(t.ok){const e=await t.json();$(e.proxies||[])}}catch(t){console.error("获取正向穿透列表失败:",t)}}function A(){const t=document.getElementById("refreshClients"),e=document.getElementById("showForwardForm"),n=document.getElementById("cancelForward"),o=document.getElementById("addForward"),s=document.getElementById("forwardForm");t==null||t.addEventListener("click",()=>{L()}),e==null||e.addEventListener("click",()=>{var a;s==null||s.classList.add("show"),(a=document.getElementById("clientsList"))!=null&&a.textContent.trim()||L()}),n==null||n.addEventListener("click",()=>{s==null||s.classList.remove("show"),I="",document.querySelectorAll(".client-card").forEach(a=>a.classList.remove("selected"))}),o==null||o.addEventListener("click",async()=>{const a=document.getElementById("forwardLocalPort"),i=document.getElementById("targetClientId"),d=document.getElementById("forwardTargetPort"),u=parseInt(a.value),f=i.value,y=parseInt(d.value);if(!u||!f||!y){c("请填写完整信息","error");return}try{const g=await(await fetch("/_ctc/forward/add",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({localPort:u,targetClientId:f,targetPort:y})})).json();g.success?(c("正向穿透已添加"),s==null||s.classList.remove("show"),a.value="",d.value="",i.value="",I="",P()):c(`添加失败: ${g.error}`,"error")}catch{c("添加失败: 网络错误","error")}});const r=document.getElementById("registerBtn");r==null||r.addEventListener("click",D)}function C(){const t=document.getElementById("cancelDelete"),e=document.getElementById("confirmDelete");t==null||t.addEventListener("click",()=>{var n;(n=document.getElementById("deleteModal"))==null||n.classList.remove("show"),p=null,h=null}),e==null||e.addEventListener("click",H),N(),A(),v(),setTimeout(()=>{E==="reverse"&&w()},500),setInterval(v,3e3),M(),E==="forward"&&(L(),P())}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",C):C();window.switchTab=j;window.showDeleteModal=R;window.selectClient=O;window.manualReconnect=_;
|