@chenpu17/tcpmapping 2.0.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 +103 -0
- package/bin/tcpmapping.js +288 -0
- package/config.json +46 -0
- package/package.json +52 -0
- package/public/dashboard.html +668 -0
- package/public/login.html +207 -0
- package/server.js +886 -0
- package/setup-password.js +203 -0
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# TCP转发服务器
|
|
2
|
+
|
|
3
|
+
一个功能强大、安全可靠的TCP端口转发服务器,支持多端口映射、WebUI管理界面和实时连接监控。
|
|
4
|
+
|
|
5
|
+
## 功能特性
|
|
6
|
+
|
|
7
|
+
### 核心功能
|
|
8
|
+
- **TCP协议端口转发** - 将TCP请求转发到后端服务器
|
|
9
|
+
- **多端口映射配置** - 同时支持多个端口转发规则
|
|
10
|
+
- **动态配置管理** - 通过WebUI实时添加/删除/启动/停止端口映射
|
|
11
|
+
- **连接监控** - 实时显示每个映射的连接数和流量统计
|
|
12
|
+
|
|
13
|
+
### WebUI管理界面
|
|
14
|
+
- 现代化响应式Web界面
|
|
15
|
+
- 实时系统状态监控(映射数、连接数、运行时间)
|
|
16
|
+
- 每个端口映射的连接数和流量统计
|
|
17
|
+
- 映射的启动/停止控制
|
|
18
|
+
|
|
19
|
+
### 安全特性
|
|
20
|
+
- 强密码策略(8字符+大小写+数字)
|
|
21
|
+
- 登录失败保护(5次失败锁定30分钟)
|
|
22
|
+
- 会话管理(24小时自动超时)
|
|
23
|
+
- bcrypt 12轮密码哈希加密
|
|
24
|
+
- 自动生成的Session Secret
|
|
25
|
+
|
|
26
|
+
## 快速开始
|
|
27
|
+
|
|
28
|
+
### 安装
|
|
29
|
+
```bash
|
|
30
|
+
# 安装依赖
|
|
31
|
+
npm install
|
|
32
|
+
|
|
33
|
+
# 设置管理员密码
|
|
34
|
+
npm run setup-password
|
|
35
|
+
|
|
36
|
+
# 启动服务器
|
|
37
|
+
npm start
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 访问
|
|
41
|
+
启动后访问 **http://localhost:9999** 进入管理界面。
|
|
42
|
+
|
|
43
|
+
## 配置说明
|
|
44
|
+
|
|
45
|
+
编辑 `config.json` 配置端口映射:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"mappings": [
|
|
50
|
+
{
|
|
51
|
+
"name": "服务名称",
|
|
52
|
+
"listen": { "host": "0.0.0.0", "port": 8080 },
|
|
53
|
+
"target": { "host": "192.168.1.100", "port": 3000 }
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"webui": { "enabled": true, "port": 9999, "host": "0.0.0.0" },
|
|
57
|
+
"logging": { "enabled": true, "level": "info" }
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 项目结构
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
├── server.js # 主服务器
|
|
65
|
+
├── config.json # 端口映射配置
|
|
66
|
+
├── auth-config.json # 认证配置(敏感)
|
|
67
|
+
├── setup-password.js # 密码设置工具
|
|
68
|
+
├── package.json # 项目配置
|
|
69
|
+
├── .gitignore # Git忽略规则
|
|
70
|
+
├── public/ # Web界面
|
|
71
|
+
│ ├── login.html # 登录页面
|
|
72
|
+
│ └── dashboard.html # 管理面板
|
|
73
|
+
└── docs/ # 文档目录
|
|
74
|
+
├── API.md # API接口文档
|
|
75
|
+
├── 功能总结.md # 功能清单
|
|
76
|
+
└── 安全配置说明.md # 安全指南
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## API接口
|
|
80
|
+
|
|
81
|
+
详细API文档请参阅 [docs/API.md](docs/API.md)
|
|
82
|
+
|
|
83
|
+
主要接口:
|
|
84
|
+
- `GET /api/health` - 健康检查(无需认证)
|
|
85
|
+
- `POST /api/login` - 登录
|
|
86
|
+
- `GET /api/mappings` - 获取映射列表(含连接数统计)
|
|
87
|
+
- `POST /api/mappings` - 添加映射
|
|
88
|
+
- `PUT /api/mappings/:id` - 更新映射
|
|
89
|
+
- `DELETE /api/mappings/:id` - 删除映射
|
|
90
|
+
- `POST /api/mappings/:id/start` - 启动映射
|
|
91
|
+
- `POST /api/mappings/:id/stop` - 停止映射
|
|
92
|
+
- `GET /api/connections` - 获取连接状态
|
|
93
|
+
- `GET /api/status` - 获取系统状态
|
|
94
|
+
|
|
95
|
+
## 技术栈
|
|
96
|
+
|
|
97
|
+
- **后端**: Node.js, Express.js, bcryptjs
|
|
98
|
+
- **前端**: 原生HTML/CSS/JavaScript
|
|
99
|
+
- **安全**: bcrypt密码哈希, Session管理
|
|
100
|
+
|
|
101
|
+
## 许可证
|
|
102
|
+
|
|
103
|
+
MIT License
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn, execSync } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
|
|
8
|
+
const APP_NAME = 'tcpmapping';
|
|
9
|
+
const PID_FILE = path.join(os.tmpdir(), 'tcpmapping.pid');
|
|
10
|
+
const LOG_FILE = path.join(os.tmpdir(), 'tcpmapping.log');
|
|
11
|
+
const SERVER_FILE = path.join(__dirname, '..', 'server.js');
|
|
12
|
+
const SETUP_PASSWORD_FILE = path.join(__dirname, '..', 'setup-password.js');
|
|
13
|
+
|
|
14
|
+
// 获取命令行参数
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
const command = args[0] || 'help';
|
|
17
|
+
|
|
18
|
+
// 颜色输出
|
|
19
|
+
const colors = {
|
|
20
|
+
green: (text) => `\x1b[32m${text}\x1b[0m`,
|
|
21
|
+
red: (text) => `\x1b[31m${text}\x1b[0m`,
|
|
22
|
+
yellow: (text) => `\x1b[33m${text}\x1b[0m`,
|
|
23
|
+
blue: (text) => `\x1b[34m${text}\x1b[0m`,
|
|
24
|
+
cyan: (text) => `\x1b[36m${text}\x1b[0m`
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// 检查进程是否运行
|
|
28
|
+
function isRunning() {
|
|
29
|
+
if (!fs.existsSync(PID_FILE)) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const pid = fs.readFileSync(PID_FILE, 'utf8').trim();
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
process.kill(parseInt(pid), 0);
|
|
37
|
+
return parseInt(pid);
|
|
38
|
+
} catch (e) {
|
|
39
|
+
// 进程不存在,清理PID文件
|
|
40
|
+
fs.unlinkSync(PID_FILE);
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 启动服务
|
|
46
|
+
function start() {
|
|
47
|
+
const pid = isRunning();
|
|
48
|
+
if (pid) {
|
|
49
|
+
console.log(colors.yellow(`${APP_NAME} is already running (PID: ${pid})`));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log(colors.blue(`Starting ${APP_NAME}...`));
|
|
54
|
+
|
|
55
|
+
const out = fs.openSync(LOG_FILE, 'a');
|
|
56
|
+
const err = fs.openSync(LOG_FILE, 'a');
|
|
57
|
+
|
|
58
|
+
const child = spawn('node', [SERVER_FILE], {
|
|
59
|
+
detached: true,
|
|
60
|
+
stdio: ['ignore', out, err],
|
|
61
|
+
cwd: path.dirname(SERVER_FILE)
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
fs.writeFileSync(PID_FILE, child.pid.toString());
|
|
65
|
+
child.unref();
|
|
66
|
+
|
|
67
|
+
console.log(colors.green(`${APP_NAME} started successfully (PID: ${child.pid})`));
|
|
68
|
+
console.log(colors.cyan(`WebUI: http://localhost:9999`));
|
|
69
|
+
console.log(colors.cyan(`Log file: ${LOG_FILE}`));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 停止服务
|
|
73
|
+
function stop() {
|
|
74
|
+
const pid = isRunning();
|
|
75
|
+
if (!pid) {
|
|
76
|
+
console.log(colors.yellow(`${APP_NAME} is not running`));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log(colors.blue(`Stopping ${APP_NAME} (PID: ${pid})...`));
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
process.kill(pid, 'SIGTERM');
|
|
84
|
+
|
|
85
|
+
// 等待进程退出
|
|
86
|
+
let attempts = 0;
|
|
87
|
+
while (attempts < 10) {
|
|
88
|
+
try {
|
|
89
|
+
process.kill(pid, 0);
|
|
90
|
+
// 进程仍在运行
|
|
91
|
+
require('child_process').execSync('sleep 0.5');
|
|
92
|
+
attempts++;
|
|
93
|
+
} catch (e) {
|
|
94
|
+
// 进程已退出
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 如果进程仍在运行,强制终止
|
|
100
|
+
try {
|
|
101
|
+
process.kill(pid, 0);
|
|
102
|
+
process.kill(pid, 'SIGKILL');
|
|
103
|
+
} catch (e) {
|
|
104
|
+
// 进程已退出
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (fs.existsSync(PID_FILE)) {
|
|
108
|
+
fs.unlinkSync(PID_FILE);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
console.log(colors.green(`${APP_NAME} stopped successfully`));
|
|
112
|
+
} catch (e) {
|
|
113
|
+
console.log(colors.red(`Failed to stop ${APP_NAME}: ${e.message}`));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 重启服务
|
|
118
|
+
function restart() {
|
|
119
|
+
console.log(colors.blue(`Restarting ${APP_NAME}...`));
|
|
120
|
+
stop();
|
|
121
|
+
setTimeout(() => {
|
|
122
|
+
start();
|
|
123
|
+
}, 1000);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 查看状态
|
|
127
|
+
function status() {
|
|
128
|
+
const pid = isRunning();
|
|
129
|
+
if (pid) {
|
|
130
|
+
console.log(colors.green(`${APP_NAME} is running (PID: ${pid})`));
|
|
131
|
+
|
|
132
|
+
// 尝试获取健康状态
|
|
133
|
+
try {
|
|
134
|
+
const http = require('http');
|
|
135
|
+
const req = http.get('http://localhost:9999/api/health', { timeout: 2000 }, (res) => {
|
|
136
|
+
let data = '';
|
|
137
|
+
res.on('data', chunk => data += chunk);
|
|
138
|
+
res.on('end', () => {
|
|
139
|
+
try {
|
|
140
|
+
const health = JSON.parse(data);
|
|
141
|
+
console.log(colors.cyan(` Status: ${health.status}`));
|
|
142
|
+
console.log(colors.cyan(` Uptime: ${Math.floor(health.uptime / 60)} minutes`));
|
|
143
|
+
console.log(colors.cyan(` Active Mappings: ${health.activeMappings}`));
|
|
144
|
+
console.log(colors.cyan(` Active Connections: ${health.activeConnections}`));
|
|
145
|
+
} catch (e) {}
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
req.on('error', () => {});
|
|
149
|
+
req.on('timeout', () => req.destroy());
|
|
150
|
+
} catch (e) {}
|
|
151
|
+
} else {
|
|
152
|
+
console.log(colors.red(`${APP_NAME} is not running`));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 查看日志
|
|
157
|
+
function logs(follow = false) {
|
|
158
|
+
if (!fs.existsSync(LOG_FILE)) {
|
|
159
|
+
console.log(colors.yellow('No log file found'));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (follow) {
|
|
164
|
+
console.log(colors.blue(`Tailing ${LOG_FILE}... (Ctrl+C to exit)`));
|
|
165
|
+
const tail = spawn('tail', ['-f', LOG_FILE], { stdio: 'inherit' });
|
|
166
|
+
process.on('SIGINT', () => {
|
|
167
|
+
tail.kill();
|
|
168
|
+
process.exit(0);
|
|
169
|
+
});
|
|
170
|
+
} else {
|
|
171
|
+
const content = fs.readFileSync(LOG_FILE, 'utf8');
|
|
172
|
+
const lines = content.split('\n').slice(-50);
|
|
173
|
+
console.log(lines.join('\n'));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// 设置密码
|
|
178
|
+
function setupPassword() {
|
|
179
|
+
console.log(colors.blue('Setting up password...'));
|
|
180
|
+
const child = spawn('node', [SETUP_PASSWORD_FILE], {
|
|
181
|
+
stdio: 'inherit',
|
|
182
|
+
cwd: path.dirname(SETUP_PASSWORD_FILE)
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
child.on('exit', (code) => {
|
|
186
|
+
process.exit(code);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 初始化配置
|
|
191
|
+
function init() {
|
|
192
|
+
const configDir = process.cwd();
|
|
193
|
+
const configFile = path.join(configDir, 'config.json');
|
|
194
|
+
|
|
195
|
+
if (fs.existsSync(configFile)) {
|
|
196
|
+
console.log(colors.yellow('config.json already exists'));
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const defaultConfig = {
|
|
201
|
+
mappings: [
|
|
202
|
+
{
|
|
203
|
+
name: "Example",
|
|
204
|
+
listen: { host: "0.0.0.0", port: 8080 },
|
|
205
|
+
target: { host: "127.0.0.1", port: 3000 }
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
webui: { enabled: true, port: 9999, host: "0.0.0.0" },
|
|
209
|
+
logging: { enabled: true, level: "info" }
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
fs.writeFileSync(configFile, JSON.stringify(defaultConfig, null, 2));
|
|
213
|
+
console.log(colors.green('Created config.json'));
|
|
214
|
+
console.log(colors.cyan('Edit config.json to configure your port mappings'));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// 显示版本
|
|
218
|
+
function version() {
|
|
219
|
+
const pkg = require(path.join(__dirname, '..', 'package.json'));
|
|
220
|
+
console.log(`${APP_NAME} v${pkg.version}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// 显示帮助
|
|
224
|
+
function help() {
|
|
225
|
+
console.log(`
|
|
226
|
+
${colors.cyan('TCP Mapping Server')} - TCP端口转发服务器
|
|
227
|
+
|
|
228
|
+
${colors.yellow('Usage:')}
|
|
229
|
+
tcpmapping <command>
|
|
230
|
+
|
|
231
|
+
${colors.yellow('Commands:')}
|
|
232
|
+
${colors.green('start')} Start the server in background
|
|
233
|
+
${colors.green('stop')} Stop the server
|
|
234
|
+
${colors.green('restart')} Restart the server
|
|
235
|
+
${colors.green('status')} Show server status
|
|
236
|
+
${colors.green('logs')} Show recent logs
|
|
237
|
+
${colors.green('logs -f')} Follow logs in real-time
|
|
238
|
+
${colors.green('setup-password')} Set up admin password
|
|
239
|
+
${colors.green('init')} Create default config.json
|
|
240
|
+
${colors.green('version')} Show version
|
|
241
|
+
${colors.green('help')} Show this help message
|
|
242
|
+
|
|
243
|
+
${colors.yellow('Examples:')}
|
|
244
|
+
tcpmapping start # Start server
|
|
245
|
+
tcpmapping status # Check if server is running
|
|
246
|
+
tcpmapping logs -f # Follow logs
|
|
247
|
+
|
|
248
|
+
${colors.yellow('WebUI:')}
|
|
249
|
+
After starting, visit http://localhost:9999
|
|
250
|
+
`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// 执行命令
|
|
254
|
+
switch (command) {
|
|
255
|
+
case 'start':
|
|
256
|
+
start();
|
|
257
|
+
break;
|
|
258
|
+
case 'stop':
|
|
259
|
+
stop();
|
|
260
|
+
break;
|
|
261
|
+
case 'restart':
|
|
262
|
+
restart();
|
|
263
|
+
break;
|
|
264
|
+
case 'status':
|
|
265
|
+
status();
|
|
266
|
+
break;
|
|
267
|
+
case 'logs':
|
|
268
|
+
logs(args[1] === '-f');
|
|
269
|
+
break;
|
|
270
|
+
case 'setup-password':
|
|
271
|
+
case 'passwd':
|
|
272
|
+
setupPassword();
|
|
273
|
+
break;
|
|
274
|
+
case 'init':
|
|
275
|
+
init();
|
|
276
|
+
break;
|
|
277
|
+
case 'version':
|
|
278
|
+
case '-v':
|
|
279
|
+
case '--version':
|
|
280
|
+
version();
|
|
281
|
+
break;
|
|
282
|
+
case 'help':
|
|
283
|
+
case '-h':
|
|
284
|
+
case '--help':
|
|
285
|
+
default:
|
|
286
|
+
help();
|
|
287
|
+
break;
|
|
288
|
+
}
|
package/config.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mappings": [
|
|
3
|
+
{
|
|
4
|
+
"name": "Dify",
|
|
5
|
+
"listen": {
|
|
6
|
+
"host": "0.0.0.0",
|
|
7
|
+
"port": 5588
|
|
8
|
+
},
|
|
9
|
+
"target": {
|
|
10
|
+
"host": "113.45.34.202",
|
|
11
|
+
"port": 5588
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "语音助手",
|
|
16
|
+
"listen": {
|
|
17
|
+
"host": "0.0.0.0",
|
|
18
|
+
"port": 3443
|
|
19
|
+
},
|
|
20
|
+
"target": {
|
|
21
|
+
"host": "115.120.49.208",
|
|
22
|
+
"port": 443
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "语音助手",
|
|
27
|
+
"listen": {
|
|
28
|
+
"host": "0.0.0.0",
|
|
29
|
+
"port": 443
|
|
30
|
+
},
|
|
31
|
+
"target": {
|
|
32
|
+
"host": "115.120.49.208",
|
|
33
|
+
"port": 443
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"webui": {
|
|
38
|
+
"enabled": true,
|
|
39
|
+
"port": 9999,
|
|
40
|
+
"host": "0.0.0.0"
|
|
41
|
+
},
|
|
42
|
+
"logging": {
|
|
43
|
+
"enabled": true,
|
|
44
|
+
"level": "info"
|
|
45
|
+
}
|
|
46
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chenpu17/tcpmapping",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "TCP端口转发服务器,支持多端口映射、WebUI管理界面和实时连接监控",
|
|
5
|
+
"main": "server.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"tcpmapping": "./bin/tcpmapping.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node server.js",
|
|
11
|
+
"dev": "node server.js",
|
|
12
|
+
"setup-password": "node setup-password.js",
|
|
13
|
+
"reset-password": "node setup-password.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"bin/",
|
|
17
|
+
"public/",
|
|
18
|
+
"server.js",
|
|
19
|
+
"setup-password.js",
|
|
20
|
+
"config.json"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"tcp",
|
|
24
|
+
"proxy",
|
|
25
|
+
"forward",
|
|
26
|
+
"port-forward",
|
|
27
|
+
"port-mapping",
|
|
28
|
+
"server",
|
|
29
|
+
"webui",
|
|
30
|
+
"cli",
|
|
31
|
+
"nodejs"
|
|
32
|
+
],
|
|
33
|
+
"author": "chenpu17",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/chenpu17/TcpMapping.git"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/chenpu17/TcpMapping/issues"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/chenpu17/TcpMapping#readme",
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=14.0.0"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"express": "^4.18.2",
|
|
48
|
+
"express-session": "^1.17.3",
|
|
49
|
+
"bcryptjs": "^2.4.3",
|
|
50
|
+
"body-parser": "^1.20.2"
|
|
51
|
+
}
|
|
52
|
+
}
|