@chenpu17/tcpmapping 2.4.0 → 2.4.1
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/CHANGELOG.md +16 -0
- package/README.md +37 -4
- package/bin/tcpmapping.js +128 -18
- package/docs/API.md +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v2.4.1
|
|
4
|
+
|
|
5
|
+
### 新增
|
|
6
|
+
|
|
7
|
+
- CLI 支持 `-p, --port <port>` 与 `-H, --host <host>` 选项,可直接指定 WebUI 端口和监听地址:
|
|
8
|
+
- `tcpmapping start --host 0.0.0.0 --port 5000` 直接对外提供服务;
|
|
9
|
+
- 选项对 `start`、`restart`、`status`、`init` 生效;
|
|
10
|
+
- 优先级:命令行选项 > 环境变量 > `config.json`。
|
|
11
|
+
- 监听 `0.0.0.0` 时,启动输出会列出 `localhost` 与本机局域网 IPv4 访问地址。
|
|
12
|
+
- 新增 `--listen` 作为 `--host` 的别名,便于记忆。
|
|
13
|
+
|
|
14
|
+
### 修复
|
|
15
|
+
|
|
16
|
+
- 使用 `WEBUI_PORT` / `WEBUI_HOST` 启动时,CLI 输出的 WebUI 地址未反映环境变量,导致提示端口与实际监听端口不一致。
|
|
17
|
+
- `init --host/--port` 现在会将指定值写入生成的 `config.json`。
|
|
18
|
+
|
|
3
19
|
## v2.4.0
|
|
4
20
|
|
|
5
21
|
### 新增
|
package/README.md
CHANGED
|
@@ -31,6 +31,14 @@ tcpmapping init # Create config file | 创建配置文件
|
|
|
31
31
|
tcpmapping start # Start server | 启动服务
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
To allow remote access on port 5000 | 如需对外提供 5000 端口访问:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
tcpmapping setup-password
|
|
38
|
+
tcpmapping init --host 0.0.0.0 --port 5000
|
|
39
|
+
tcpmapping start --host 0.0.0.0 --port 5000
|
|
40
|
+
```
|
|
41
|
+
|
|
34
42
|
### Local Install | 本地安装
|
|
35
43
|
|
|
36
44
|
```bash
|
|
@@ -56,6 +64,23 @@ npm start
|
|
|
56
64
|
| `tcpmapping setup-password` | Set admin password | 设置管理员密码 |
|
|
57
65
|
| `tcpmapping init` | Create config file | 创建配置文件 |
|
|
58
66
|
|
|
67
|
+
### CLI Options | 命令行选项
|
|
68
|
+
|
|
69
|
+
| Option | Description |
|
|
70
|
+
|--------|-------------|
|
|
71
|
+
| `-p, --port <port>` | WebUI port (default: 9999) | WebUI端口(默认9999) |
|
|
72
|
+
| `-H, --host <host>` | Listen address (default: 127.0.0.1, use 0.0.0.0 for remote access) | 监听地址(默认127.0.0.1,远程访问用0.0.0.0) |
|
|
73
|
+
|
|
74
|
+
Options apply to `start`, `restart`, `status` and `init`, and take precedence over environment variables and `config.json`.
|
|
75
|
+
|
|
76
|
+
选项对 `start`、`restart`、`status`、`init` 生效,优先级高于环境变量和 `config.json`。
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
tcpmapping start --host 0.0.0.0 --port 5000 # Public access on port 5000 | 对外监听 5000
|
|
80
|
+
tcpmapping status --port 5000 # Check that instance | 查看该实例状态
|
|
81
|
+
tcpmapping restart --port 5000 # Restart with the same port | 以该端口重启
|
|
82
|
+
```
|
|
83
|
+
|
|
59
84
|
---
|
|
60
85
|
|
|
61
86
|
## WebUI
|
|
@@ -83,9 +108,9 @@ After starting, visit **http://localhost:9999**
|
|
|
83
108
|
|
|
84
109
|
Default username | 默认用户名: `admin`
|
|
85
110
|
|
|
86
|
-
> For security, the WebUI listens on `127.0.0.1` by default. To access it from another machine,
|
|
111
|
+
> For security, the WebUI listens on `127.0.0.1` by default. To access it from another machine, start with `--host 0.0.0.0` (or set `webui.host` in `config.json`) and restrict access with a firewall.
|
|
87
112
|
>
|
|
88
|
-
> 出于安全考虑,WebUI 默认只监听 `127.0.0.1
|
|
113
|
+
> 出于安全考虑,WebUI 默认只监听 `127.0.0.1`。如需远程访问,请使用 `--host 0.0.0.0` 启动(或修改 `config.json` 中的 `webui.host`),并通过防火墙限制访问来源。公网部署请务必设置强密码,建议使用 HTTPS 反向代理。
|
|
89
114
|
|
|
90
115
|
---
|
|
91
116
|
|
|
@@ -147,6 +172,10 @@ For remote WebUI access, set `webui.host` to `"0.0.0.0"` and protect the port wi
|
|
|
147
172
|
| `WEBUI_HOST` | Override the WebUI bind address | 覆盖WebUI监听地址 |
|
|
148
173
|
| `SESSION_SECRET` | Override the session secret | 覆盖会话密钥 |
|
|
149
174
|
|
|
175
|
+
Priority | 优先级: CLI options > environment variables > `config.json` default.
|
|
176
|
+
|
|
177
|
+
优先级:命令行选项 > 环境变量 > `config.json` 默认值。
|
|
178
|
+
|
|
150
179
|
---
|
|
151
180
|
|
|
152
181
|
## Data Persistence | 数据持久化
|
|
@@ -223,9 +252,13 @@ tcpmapping status
|
|
|
223
252
|
|
|
224
253
|
### Port already in use? | 端口被占用?
|
|
225
254
|
|
|
226
|
-
|
|
255
|
+
Start with another port, or edit `webui.port` / `mappings[].listen.port` in config.json.
|
|
256
|
+
|
|
257
|
+
使用 `--port` 指定其他端口启动,或修改 config.json 中的 `webui.port` / `mappings[].listen.port`。
|
|
227
258
|
|
|
228
|
-
|
|
259
|
+
```bash
|
|
260
|
+
tcpmapping restart --port 8080
|
|
261
|
+
```
|
|
229
262
|
|
|
230
263
|
---
|
|
231
264
|
|
package/bin/tcpmapping.js
CHANGED
|
@@ -5,6 +5,7 @@ const path = require('path');
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const os = require('os');
|
|
7
7
|
const http = require('http');
|
|
8
|
+
const net = require('net');
|
|
8
9
|
|
|
9
10
|
const APP_NAME = 'tcpmapping';
|
|
10
11
|
const DATA_DIR = process.env.TCPMAPPING_DATA_DIR || path.join(os.homedir(), '.tcpmapping');
|
|
@@ -13,10 +14,45 @@ const LOG_FILE = path.join(DATA_DIR, 'tcpmapping.log');
|
|
|
13
14
|
const SERVER_FILE = path.join(__dirname, '..', 'server.js');
|
|
14
15
|
const SETUP_PASSWORD_FILE = path.join(__dirname, '..', 'setup-password.js');
|
|
15
16
|
|
|
16
|
-
//
|
|
17
|
-
|
|
17
|
+
// 解析命令行参数
|
|
18
|
+
function parseArgs(argv) {
|
|
19
|
+
const options = { port: null, host: null };
|
|
20
|
+
const positional = [];
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < argv.length; i++) {
|
|
23
|
+
const arg = argv[i];
|
|
24
|
+
|
|
25
|
+
if (arg === '--port' || arg === '-p') {
|
|
26
|
+
options.port = argv[++i];
|
|
27
|
+
} else if (arg.startsWith('--port=')) {
|
|
28
|
+
options.port = arg.slice('--port='.length);
|
|
29
|
+
} else if (arg === '--host' || arg === '-H' || arg === '--listen') {
|
|
30
|
+
options.host = argv[++i];
|
|
31
|
+
} else if (arg.startsWith('--host=')) {
|
|
32
|
+
options.host = arg.slice('--host='.length);
|
|
33
|
+
} else if (arg.startsWith('--listen=')) {
|
|
34
|
+
options.host = arg.slice('--listen='.length);
|
|
35
|
+
} else {
|
|
36
|
+
positional.push(arg);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return { options, positional };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const { options: webuiOptions, positional: args } = parseArgs(process.argv.slice(2));
|
|
18
44
|
const command = args[0] || 'help';
|
|
19
45
|
|
|
46
|
+
if (webuiOptions.port !== null && !isValidPort(webuiOptions.port)) {
|
|
47
|
+
console.error(`无效的端口: ${webuiOptions.port}(应为 1-65535)`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (webuiOptions.host !== null && !isValidHost(webuiOptions.host)) {
|
|
52
|
+
console.error(`无效的监听地址: ${webuiOptions.host}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
20
56
|
// 颜色输出(非 TTY 或设置了 NO_COLOR 时自动关闭)
|
|
21
57
|
const useColor = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
|
|
22
58
|
const colorize = (code) => (text) => useColor ? `\x1b[${code}m${text}\x1b[0m` : String(text);
|
|
@@ -82,15 +118,51 @@ function getAuthConfigPath() {
|
|
|
82
118
|
return null;
|
|
83
119
|
}
|
|
84
120
|
|
|
85
|
-
//
|
|
121
|
+
// 验证端口
|
|
122
|
+
function isValidPort(port) {
|
|
123
|
+
const p = Number(port);
|
|
124
|
+
return Number.isInteger(p) && p >= 1 && p <= 65535;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 验证 IP 地址或主机名(与 server.js 保持一致)
|
|
128
|
+
function isValidHost(host) {
|
|
129
|
+
if (!host || typeof host !== 'string') return false;
|
|
130
|
+
|
|
131
|
+
const value = host.trim();
|
|
132
|
+
if (!value || value.length > 253) return false;
|
|
133
|
+
if (net.isIP(value)) return true;
|
|
134
|
+
|
|
135
|
+
const hostnamePattern = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
136
|
+
return hostnamePattern.test(value);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 获取 WebUI 访问信息(优先级:命令行参数 > 环境变量 > 配置文件)
|
|
86
140
|
function getWebuiInfo() {
|
|
87
141
|
const config = readConfig().data;
|
|
88
142
|
const webui = (config && config.webui) || {};
|
|
89
143
|
|
|
144
|
+
let host = webui.host || '127.0.0.1';
|
|
145
|
+
let port = Number(webui.port) || 9999;
|
|
146
|
+
|
|
147
|
+
if (process.env.WEBUI_PORT && isValidPort(process.env.WEBUI_PORT)) {
|
|
148
|
+
port = Number(process.env.WEBUI_PORT);
|
|
149
|
+
}
|
|
150
|
+
if (process.env.WEBUI_HOST && isValidHost(process.env.WEBUI_HOST)) {
|
|
151
|
+
host = process.env.WEBUI_HOST.trim();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 命令行参数优先级最高
|
|
155
|
+
if (webuiOptions.port !== null) {
|
|
156
|
+
port = Number(webuiOptions.port);
|
|
157
|
+
}
|
|
158
|
+
if (webuiOptions.host !== null) {
|
|
159
|
+
host = webuiOptions.host.trim();
|
|
160
|
+
}
|
|
161
|
+
|
|
90
162
|
return {
|
|
91
163
|
enabled: webui.enabled !== false,
|
|
92
|
-
host
|
|
93
|
-
port
|
|
164
|
+
host,
|
|
165
|
+
port
|
|
94
166
|
};
|
|
95
167
|
}
|
|
96
168
|
|
|
@@ -105,6 +177,24 @@ function getAccessHost(host) {
|
|
|
105
177
|
return host;
|
|
106
178
|
}
|
|
107
179
|
|
|
180
|
+
// 列出可访问地址(监听所有网卡时包含本机局域网 IP,便于远程访问)
|
|
181
|
+
function getAccessHosts(host) {
|
|
182
|
+
if (host !== '0.0.0.0' && host !== '::') {
|
|
183
|
+
return [getAccessHost(host)];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const hosts = ['localhost'];
|
|
187
|
+
const interfaces = os.networkInterfaces();
|
|
188
|
+
for (const entries of Object.values(interfaces)) {
|
|
189
|
+
for (const entry of entries || []) {
|
|
190
|
+
if (entry.family === 'IPv4' && !entry.internal) {
|
|
191
|
+
hosts.push(entry.address);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return hosts;
|
|
196
|
+
}
|
|
197
|
+
|
|
108
198
|
// 检查进程是否存在
|
|
109
199
|
function isAlive(pid) {
|
|
110
200
|
try {
|
|
@@ -172,6 +262,7 @@ async function start() {
|
|
|
172
262
|
const pid = isRunning();
|
|
173
263
|
if (pid) {
|
|
174
264
|
console.log(colors.yellow(`${APP_NAME} 已在运行 (PID: ${pid})`));
|
|
265
|
+
console.log(colors.gray('如需修改端口或监听地址,请使用: tcpmapping restart --port <port> --host <host>'));
|
|
175
266
|
return;
|
|
176
267
|
}
|
|
177
268
|
|
|
@@ -184,6 +275,9 @@ async function start() {
|
|
|
184
275
|
ensureDataDir();
|
|
185
276
|
console.log(colors.blue(`正在启动 ${APP_NAME}...`));
|
|
186
277
|
|
|
278
|
+
// 将生效的 WebUI 地址显式传给子进程,确保实际监听与提示一致
|
|
279
|
+
const webui = getWebuiInfo();
|
|
280
|
+
|
|
187
281
|
const out = fs.openSync(LOG_FILE, 'a');
|
|
188
282
|
const err = fs.openSync(LOG_FILE, 'a');
|
|
189
283
|
|
|
@@ -193,7 +287,12 @@ async function start() {
|
|
|
193
287
|
detached: true,
|
|
194
288
|
stdio: ['ignore', out, err],
|
|
195
289
|
// 使用当前目录,以便 server 可以找到当前目录的 config.json
|
|
196
|
-
cwd: process.cwd()
|
|
290
|
+
cwd: process.cwd(),
|
|
291
|
+
env: {
|
|
292
|
+
...process.env,
|
|
293
|
+
WEBUI_HOST: webui.host,
|
|
294
|
+
WEBUI_PORT: String(webui.port)
|
|
295
|
+
}
|
|
197
296
|
});
|
|
198
297
|
} finally {
|
|
199
298
|
// 子进程已继承文件描述符,父进程可以关闭
|
|
@@ -224,10 +323,10 @@ async function start() {
|
|
|
224
323
|
return;
|
|
225
324
|
}
|
|
226
325
|
|
|
227
|
-
const webui = getWebuiInfo();
|
|
228
326
|
console.log(colors.green(`${APP_NAME} 启动成功 (PID: ${child.pid})`));
|
|
229
327
|
if (webui.enabled) {
|
|
230
|
-
|
|
328
|
+
const urls = getAccessHosts(webui.host).map(host => `http://${host}:${webui.port}`);
|
|
329
|
+
console.log(colors.cyan(`WebUI: ${urls.join('\n ')}`));
|
|
231
330
|
if (webui.host === '0.0.0.0' || webui.host === '::') {
|
|
232
331
|
console.log(colors.yellow('提示: WebUI 监听在所有网卡上,请确保使用强密码'));
|
|
233
332
|
}
|
|
@@ -335,7 +434,7 @@ async function status() {
|
|
|
335
434
|
const health = await fetchHealth(host, webui.port);
|
|
336
435
|
|
|
337
436
|
if (health) {
|
|
338
|
-
console.log(` WebUI:
|
|
437
|
+
console.log(` WebUI: ${getAccessHosts(webui.host).map(item => `http://${item}:${webui.port}`).join(' ')}`);
|
|
339
438
|
console.log(` 运行时间: ${Math.floor(health.uptime / 60)} 分钟`);
|
|
340
439
|
console.log(` 活跃映射: ${health.activeMappings}`);
|
|
341
440
|
console.log(` 活跃连接: ${health.activeConnections}`);
|
|
@@ -463,9 +562,9 @@ function init() {
|
|
|
463
562
|
mappings: [],
|
|
464
563
|
webui: {
|
|
465
564
|
enabled: true,
|
|
466
|
-
//
|
|
467
|
-
host: '127.0.0.1',
|
|
468
|
-
port: 9999
|
|
565
|
+
// 默认只监听本机;如需远程访问管理面板,请使用 --host 0.0.0.0 并配置防火墙
|
|
566
|
+
host: webuiOptions.host ? webuiOptions.host.trim() : '127.0.0.1',
|
|
567
|
+
port: webuiOptions.port !== null ? Number(webuiOptions.port) : 9999
|
|
469
568
|
},
|
|
470
569
|
logging: {
|
|
471
570
|
enabled: true,
|
|
@@ -496,7 +595,7 @@ function help() {
|
|
|
496
595
|
${colors.cyan('TCP Mapping Server')} - TCP端口转发服务器
|
|
497
596
|
|
|
498
597
|
${colors.yellow('用法:')}
|
|
499
|
-
tcpmapping <command>
|
|
598
|
+
tcpmapping <command> [options]
|
|
500
599
|
|
|
501
600
|
${colors.yellow('命令:')}
|
|
502
601
|
${colors.green('start')} 后台启动服务
|
|
@@ -510,18 +609,29 @@ ${colors.yellow('命令:')}
|
|
|
510
609
|
${colors.green('version')} 显示版本
|
|
511
610
|
${colors.green('help')} 显示帮助
|
|
512
611
|
|
|
612
|
+
${colors.yellow('选项:')}
|
|
613
|
+
${colors.green('-p, --port <port>')} WebUI 端口(默认 9999)
|
|
614
|
+
${colors.green('-H, --host <host>')} 监听地址(默认 127.0.0.1;公网访问用 0.0.0.0,别名 --listen)
|
|
615
|
+
|
|
616
|
+
选项对 start / restart / status / init 生效,优先级高于环境变量和配置文件。
|
|
617
|
+
|
|
513
618
|
${colors.yellow('配置路径(按优先级):')}
|
|
514
619
|
1. ./config.json 当前目录(项目配置)
|
|
515
620
|
2. ~/.tcpmapping/ 用户数据目录(全局配置)
|
|
516
621
|
|
|
622
|
+
${colors.yellow('环境变量:')}
|
|
623
|
+
WEBUI_PORT / WEBUI_HOST 等效于 --port / --host
|
|
624
|
+
|
|
517
625
|
${colors.yellow('示例:')}
|
|
518
|
-
tcpmapping init
|
|
519
|
-
tcpmapping setup-password
|
|
520
|
-
tcpmapping start
|
|
521
|
-
tcpmapping status
|
|
626
|
+
tcpmapping init --host 0.0.0.0 --port 5000 # 初始化并允许远程访问
|
|
627
|
+
tcpmapping setup-password # 设置管理员密码
|
|
628
|
+
tcpmapping start --host 0.0.0.0 --port 5000 # 对外监听 5000 端口
|
|
629
|
+
tcpmapping status --port 5000 # 查看 5000 端口上的服务状态
|
|
630
|
+
tcpmapping restart --port 5000 # 以新端口重启
|
|
522
631
|
|
|
523
632
|
${colors.yellow('WebUI:')}
|
|
524
|
-
|
|
633
|
+
默认访问 http://localhost:9999;使用 --host 0.0.0.0 后可通过公网 IP 访问,
|
|
634
|
+
请务必设置强密码,并建议配合防火墙或 HTTPS 反向代理使用。
|
|
525
635
|
`);
|
|
526
636
|
}
|
|
527
637
|
|
package/docs/API.md
CHANGED
|
@@ -25,7 +25,7 @@ GET /api/health
|
|
|
25
25
|
```json
|
|
26
26
|
{
|
|
27
27
|
"status": "ok",
|
|
28
|
-
"version": "2.4.
|
|
28
|
+
"version": "2.4.1",
|
|
29
29
|
"timestamp": "2025-12-08T08:30:00.000Z",
|
|
30
30
|
"uptime": 3600,
|
|
31
31
|
"activeMappings": 3,
|
|
@@ -396,7 +396,7 @@ GET /api/status
|
|
|
396
396
|
{
|
|
397
397
|
"success": true,
|
|
398
398
|
"data": {
|
|
399
|
-
"version": "2.4.
|
|
399
|
+
"version": "2.4.1",
|
|
400
400
|
"username": "admin",
|
|
401
401
|
"totalMappings": 3,
|
|
402
402
|
"activeMappings": 3,
|