@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 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, set `webui.host` to `0.0.0.0` in `config.json` and restrict access with a firewall.
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`。如需远程访问,请将 `config.json` 中的 `webui.host` 改为 `0.0.0.0`,并通过防火墙限制访问来源。
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
- Edit `webui.port` or `mappings[].listen.port` in config.json.
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
- 修改 config.json 中的 `webui.port` 或 `mappings[].listen.port`。
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
- const args = process.argv.slice(2);
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
- // 获取 WebUI 访问信息
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: webui.host || '127.0.0.1',
93
- port: Number(webui.port) || 9999
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
- console.log(colors.cyan(`WebUI: http://${getAccessHost(webui.host)}:${webui.port}`));
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: http://${getAccessHost(webui.host)}:${webui.port}`);
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
- // 默认只监听本机;如需远程访问管理面板,请改为 "0.0.0.0" 并配置防火墙
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
- 启动后访问 http://localhost:9999(默认仅本机可访问)
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.0",
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.0",
399
+ "version": "2.4.1",
400
400
  "username": "admin",
401
401
  "totalMappings": 3,
402
402
  "activeMappings": 3,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chenpu17/tcpmapping",
3
- "version": "2.4.0",
3
+ "version": "2.4.1",
4
4
  "description": "TCP端口转发服务器,支持多端口映射、WebUI管理界面和实时连接监控",
5
5
  "main": "server.js",
6
6
  "bin": {