@chenpu17/tcpmapping 2.1.0 → 2.4.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/docs/API.md ADDED
@@ -0,0 +1,532 @@
1
+ # API 接口文档
2
+
3
+ ## 概述
4
+
5
+ TCP转发服务器提供RESTful API接口,用于管理端口映射、监控连接状态和系统管理。
6
+
7
+ - **Base URL**: `http://localhost:9999`
8
+ - **认证方式**: Session Cookie
9
+ - **数据格式**: JSON
10
+ - **提示语言**: 可选请求头 `X-Language: zh-CN | en-US`,未提供时根据 `Accept-Language` 自动选择;影响响应中的 `message` 字段。
11
+
12
+ ---
13
+
14
+ ## 公开接口(无需认证)
15
+
16
+ ### 健康检查
17
+
18
+ 检查服务器运行状态,用于监控和负载均衡。
19
+
20
+ ```
21
+ GET /api/health
22
+ ```
23
+
24
+ **响应示例**:
25
+ ```json
26
+ {
27
+ "status": "ok",
28
+ "version": "2.4.0",
29
+ "timestamp": "2025-12-08T08:30:00.000Z",
30
+ "uptime": 3600,
31
+ "activeMappings": 3,
32
+ "activeConnections": 5
33
+ }
34
+ ```
35
+
36
+ ---
37
+
38
+ ## 认证接口
39
+
40
+ ### 登录
41
+
42
+ ```
43
+ POST /api/login
44
+ Content-Type: application/json
45
+ ```
46
+
47
+ **请求参数**:
48
+ | 参数 | 类型 | 必填 | 说明 |
49
+ |------|------|------|------|
50
+ | username | string | 是 | 用户名 |
51
+ | password | string | 是 | 密码 |
52
+
53
+ **请求示例**:
54
+ ```json
55
+ {
56
+ "username": "admin",
57
+ "password": "YourPassword123"
58
+ }
59
+ ```
60
+
61
+ **响应示例**:
62
+ ```json
63
+ {
64
+ "success": true,
65
+ "message": "登录成功"
66
+ }
67
+ ```
68
+
69
+ **错误响应**:
70
+ - `401`: 用户名或密码错误
71
+ - `429`: 登录尝试次数过多,账户已锁定
72
+ - `503`: 服务器尚未配置管理员密码(请运行 `tcpmapping setup-password`)
73
+
74
+ > 登录成功后会重新生成 Session ID。所有写操作(POST/PUT/DELETE)如果携带 `Origin` 请求头,其 Host 必须与当前服务一致,否则返回 `403`。
75
+
76
+ ### 登出
77
+
78
+ ```
79
+ POST /api/logout
80
+ ```
81
+
82
+ **响应示例**:
83
+ ```json
84
+ {
85
+ "success": true,
86
+ "message": "已登出"
87
+ }
88
+ ```
89
+
90
+ ---
91
+
92
+ ## 端口映射管理(需要认证)
93
+
94
+ ### 获取映射列表
95
+
96
+ ```
97
+ GET /api/mappings
98
+ ```
99
+
100
+ **响应示例**:
101
+ ```json
102
+ {
103
+ "success": true,
104
+ "data": [
105
+ {
106
+ "id": "miww2f23ponqwz",
107
+ "name": "Dify",
108
+ "listen": {
109
+ "host": "0.0.0.0",
110
+ "port": 5588
111
+ },
112
+ "target": {
113
+ "host": "113.45.34.202",
114
+ "port": 5588
115
+ },
116
+ "status": "running",
117
+ "connectionCount": 3,
118
+ "bytesReceived": 102400,
119
+ "bytesSent": 204800,
120
+ "totalConnections": 42,
121
+ "totalBytesReceived": 1048576,
122
+ "totalBytesSent": 2097152
123
+ }
124
+ ]
125
+ }
126
+ ```
127
+
128
+ **字段说明**:
129
+ | 字段 | 类型 | 说明 |
130
+ |------|------|------|
131
+ | id | string | 映射唯一标识符 |
132
+ | name | string | 映射名称 |
133
+ | listen.host | string | 监听主机地址 |
134
+ | listen.port | number | 监听端口 |
135
+ | target.host | string | 目标主机地址 |
136
+ | target.port | number | 目标端口 |
137
+ | status | string | 状态: running / stopped |
138
+ | connectionCount | number | 当前活跃连接数 |
139
+ | bytesReceived | number | 当前活跃连接的上行流量(字节) |
140
+ | bytesSent | number | 当前活跃连接的下行流量(字节) |
141
+ | totalConnections | number | 累计连接数(服务启动以来) |
142
+ | totalBytesReceived | number | 累计上行流量(字节) |
143
+ | totalBytesSent | number | 累计下行流量(字节) |
144
+
145
+ > 累计统计保存在内存中,服务重启后归零。
146
+
147
+ ### 添加映射
148
+
149
+ ```
150
+ POST /api/mappings
151
+ Content-Type: application/json
152
+ ```
153
+
154
+ **请求参数**:
155
+ | 参数 | 类型 | 必填 | 说明 |
156
+ |------|------|------|------|
157
+ | name | string | 是 | 映射名称 |
158
+ | listen.host | string | 否 | 监听地址,默认 0.0.0.0 |
159
+ | listen.port | number | 是 | 监听端口 (1-65535) |
160
+ | target.host | string | 是 | 目标主机地址 |
161
+ | target.port | number | 是 | 目标端口 (1-65535) |
162
+
163
+ **请求示例**:
164
+ ```json
165
+ {
166
+ "name": "Web服务",
167
+ "listen": {
168
+ "port": 8080
169
+ },
170
+ "target": {
171
+ "host": "192.168.1.100",
172
+ "port": 3000
173
+ }
174
+ }
175
+ ```
176
+
177
+ **响应示例**:
178
+ ```json
179
+ {
180
+ "success": true,
181
+ "message": "端口映射已启动",
182
+ "data": {
183
+ "id": "abc123xyz",
184
+ "name": "Web服务",
185
+ "listen": {
186
+ "host": "0.0.0.0",
187
+ "port": 8080
188
+ },
189
+ "target": {
190
+ "host": "192.168.1.100",
191
+ "port": 3000
192
+ }
193
+ }
194
+ }
195
+ ```
196
+
197
+ **验证规则**:
198
+ - 映射名称不能为空,且不超过 100 个字符
199
+ - 端口必须在 1-65535 范围内
200
+ - 监听地址和目标主机必须是有效的 IP 地址或域名
201
+ - 监听地址 + 端口不能与已有映射重复
202
+ - 端口被占用时会返回 `400`,且不会写入配置
203
+
204
+ ### 更新映射
205
+
206
+ ```
207
+ PUT /api/mappings/:identifier
208
+ Content-Type: application/json
209
+ ```
210
+
211
+ **路径参数**:
212
+ - `identifier`: 映射ID或索引
213
+
214
+ **请求示例**:
215
+ ```json
216
+ {
217
+ "name": "新名称",
218
+ "target": {
219
+ "host": "192.168.1.200",
220
+ "port": 3001
221
+ }
222
+ }
223
+ ```
224
+
225
+ **响应示例**:
226
+ ```json
227
+ {
228
+ "success": true,
229
+ "message": "端口映射已更新",
230
+ "data": {
231
+ "id": "abc123xyz",
232
+ "name": "新名称",
233
+ "listen": {
234
+ "host": "0.0.0.0",
235
+ "port": 8080
236
+ },
237
+ "target": {
238
+ "host": "192.168.1.200",
239
+ "port": 3001
240
+ }
241
+ }
242
+ }
243
+ ```
244
+
245
+ > 只更新请求中提供的字段。若映射原本处于停止状态,更新后仍保持停止。
246
+ > 仅修改名称或目标地址时不会重启监听,已有连接不受影响(新连接使用新目标);修改监听地址或端口时会重新绑定,已有连接会被断开。
247
+
248
+ ### 删除映射
249
+
250
+ ```
251
+ DELETE /api/mappings/:identifier
252
+ ```
253
+
254
+ **路径参数**:
255
+ - `identifier`: 映射ID或索引
256
+
257
+ **响应示例**:
258
+ ```json
259
+ {
260
+ "success": true,
261
+ "message": "端口映射已删除"
262
+ }
263
+ ```
264
+
265
+ ### 启动映射
266
+
267
+ ```
268
+ POST /api/mappings/:identifier/start
269
+ ```
270
+
271
+ **响应示例**:
272
+ ```json
273
+ {
274
+ "success": true,
275
+ "message": "端口映射已启动"
276
+ }
277
+ ```
278
+
279
+ ### 停止映射
280
+
281
+ ```
282
+ POST /api/mappings/:identifier/stop
283
+ ```
284
+
285
+ **响应示例**:
286
+ ```json
287
+ {
288
+ "success": true,
289
+ "message": "端口映射已停止"
290
+ }
291
+ ```
292
+
293
+ ---
294
+
295
+ ## 监控接口(需要认证)
296
+
297
+ ### 获取连接状态
298
+
299
+ ```
300
+ GET /api/connections
301
+ ```
302
+
303
+ **响应示例**:
304
+ ```json
305
+ {
306
+ "success": true,
307
+ "data": {
308
+ "total": 5,
309
+ "byPort": [
310
+ {
311
+ "host": "0.0.0.0",
312
+ "port": 5588,
313
+ "mappingName": "Dify",
314
+ "totalConnections": 3,
315
+ "connections": [
316
+ {
317
+ "id": "conn123",
318
+ "mappingName": "Dify",
319
+ "sourceHost": "0.0.0.0",
320
+ "sourcePort": 5588,
321
+ "listenAddress": "192.168.1.5:5588",
322
+ "clientAddress": "192.168.1.10:54321",
323
+ "targetAddress": "113.45.34.202:5588",
324
+ "startTime": "2025-12-08T08:00:00.000Z",
325
+ "lastActivity": 1733648400000,
326
+ "durationMs": 120000,
327
+ "bytesReceived": 10240,
328
+ "bytesSent": 20480,
329
+ "status": "connected"
330
+ }
331
+ ]
332
+ }
333
+ ]
334
+ }
335
+ }
336
+ ```
337
+
338
+ **字段说明**:
339
+ | 字段 | 类型 | 说明 |
340
+ |------|------|------|
341
+ | listenAddress | string | 客户端实际连接到的本地地址(如配置为 `0.0.0.0`,这里显示真实网卡地址) |
342
+ | clientAddress | string | 客户端源地址(IP:端口) |
343
+ | targetAddress | string | 转发目的地址(IP:端口) |
344
+ | durationMs | number | 连接已持续时长(毫秒) |
345
+
346
+ ### 获取最近连接记录
347
+
348
+ ```
349
+ GET /api/connections/history?mappingId=<id>&limit=100
350
+ ```
351
+
352
+ **查询参数**:
353
+ | 参数 | 类型 | 必填 | 说明 |
354
+ |------|------|------|------|
355
+ | mappingId | string | 否 | 只返回指定映射的记录 |
356
+ | limit | number | 否 | 返回条数,默认 50,最大 200 |
357
+
358
+ **响应示例**:
359
+ ```json
360
+ {
361
+ "success": true,
362
+ "data": {
363
+ "total": 120,
364
+ "limit": 100,
365
+ "items": [
366
+ {
367
+ "id": "conn123",
368
+ "mappingId": "abc123xyz",
369
+ "mappingName": "Web服务",
370
+ "listenAddress": "192.168.1.5:5588",
371
+ "clientAddress": "192.168.1.10:54321",
372
+ "targetAddress": "113.45.34.202:5588",
373
+ "startTime": "2025-12-08T08:00:00.000Z",
374
+ "endTime": "2025-12-08T08:02:00.000Z",
375
+ "durationMs": 120000,
376
+ "bytesReceived": 10240,
377
+ "bytesSent": 20480,
378
+ "status": "connected",
379
+ "closeReason": "客户端关闭"
380
+ }
381
+ ]
382
+ }
383
+ }
384
+ ```
385
+
386
+ > 记录只在内存中保留最近 500 条已关闭连接,服务重启后清空;通过 CLI(`tcpmapping start`)启动时,连接关闭日志会同时写入日志文件。`status` 为 `connecting` 表示目标连接尚未建立即结束。
387
+
388
+ ### 获取系统状态
389
+
390
+ ```
391
+ GET /api/status
392
+ ```
393
+
394
+ **响应示例**:
395
+ ```json
396
+ {
397
+ "success": true,
398
+ "data": {
399
+ "version": "2.4.0",
400
+ "username": "admin",
401
+ "totalMappings": 3,
402
+ "activeMappings": 3,
403
+ "totalConnections": 5,
404
+ "uptime": 3600.5,
405
+ "memoryUsage": {
406
+ "rss": 51544064,
407
+ "heapTotal": 10272768,
408
+ "heapUsed": 8239376,
409
+ "external": 2104438,
410
+ "arrayBuffers": 18635
411
+ },
412
+ "resources": {
413
+ "cpuPercent": 1.25,
414
+ "cpuCount": 8,
415
+ "memory": {
416
+ "rss": 51544064,
417
+ "heapTotal": 10272768,
418
+ "heapUsed": 8239376,
419
+ "external": 2104438,
420
+ "arrayBuffers": 18635,
421
+ "systemTotal": 17179869184,
422
+ "systemFree": 4294967296
423
+ },
424
+ "traffic": {
425
+ "receivedPerSecond": 20480,
426
+ "sentPerSecond": 18240,
427
+ "totalReceived": 104857600,
428
+ "totalSent": 99614720
429
+ },
430
+ "sampledAt": "2025-12-08T08:30:00.000Z"
431
+ }
432
+ }
433
+ }
434
+ ```
435
+
436
+ **`resources` 字段说明**:
437
+
438
+ | 字段 | 说明 |
439
+ |------|------|
440
+ | `cpuPercent` | 服务进程 CPU 使用率,已按核心数归一化到 0-100(占整机比例),每秒采样一次 |
441
+ | `cpuCount` | 可用 CPU 核心数 |
442
+ | `memory.rss` | 进程常驻内存(RSS) |
443
+ | `memory.heapUsed` / `heapTotal` | V8 堆已用 / 已申请内存 |
444
+ | `memory.systemTotal` / `systemFree` | 系统总内存 / 当前可用内存,用于计算进程占系统内存的比例 |
445
+ | `traffic.receivedPerSecond` | 实时上行速率(客户端 → 目标),字节/秒,按采样间隔计算 |
446
+ | `traffic.sentPerSecond` | 实时下行速率(目标 → 客户端),字节/秒 |
447
+ | `traffic.totalReceived` / `totalSent` | 进程启动以来的累计上下行流量,映射被删除后仍然保留 |
448
+ | `sampledAt` | 最近一次采样时间(ISO 8601) |
449
+
450
+ > 资源数据由进程内每秒采样器维护;`cpuPercent` 是整机占比(单核满载为 `100 / cpuCount`%),不是单核使用率。
451
+
452
+ ---
453
+
454
+ ## 错误响应
455
+
456
+ 所有API在发生错误时返回统一格式:
457
+
458
+ ```json
459
+ {
460
+ "success": false,
461
+ "message": "错误描述信息"
462
+ }
463
+ ```
464
+
465
+ **HTTP状态码**:
466
+ | 状态码 | 说明 |
467
+ |--------|------|
468
+ | 200 | 成功 |
469
+ | 400 | 请求参数错误 / 端口被占用 / 配置保存失败 |
470
+ | 401 | 未认证 / 认证失败 |
471
+ | 403 | 跨站请求被拒绝 |
472
+ | 404 | 资源不存在 |
473
+ | 429 | 请求过于频繁 |
474
+ | 500 | 服务器内部错误 |
475
+ | 503 | 尚未配置管理员密码 |
476
+
477
+ ---
478
+
479
+ ## 使用示例
480
+
481
+ ### cURL 示例
482
+
483
+ ```bash
484
+ # 登录
485
+ curl -X POST http://localhost:9999/api/login \
486
+ -H "Content-Type: application/json" \
487
+ -d '{"username":"admin","password":"YourPassword"}' \
488
+ -c cookies.txt
489
+
490
+ # 获取映射列表
491
+ curl http://localhost:9999/api/mappings -b cookies.txt
492
+
493
+ # 添加映射
494
+ curl -X POST http://localhost:9999/api/mappings \
495
+ -H "Content-Type: application/json" \
496
+ -d '{"name":"测试","listen":{"port":8888},"target":{"host":"127.0.0.1","port":80}}' \
497
+ -b cookies.txt
498
+
499
+ # 停止映射
500
+ curl -X POST http://localhost:9999/api/mappings/0/stop -b cookies.txt
501
+
502
+ # 删除映射
503
+ curl -X DELETE http://localhost:9999/api/mappings/0 -b cookies.txt
504
+
505
+ # 健康检查
506
+ curl http://localhost:9999/api/health
507
+ ```
508
+
509
+ ### JavaScript 示例
510
+
511
+ ```javascript
512
+ // 登录
513
+ const login = await fetch('/api/login', {
514
+ method: 'POST',
515
+ headers: { 'Content-Type': 'application/json' },
516
+ body: JSON.stringify({ username: 'admin', password: 'YourPassword' })
517
+ });
518
+
519
+ // 获取映射列表
520
+ const mappings = await fetch('/api/mappings').then(r => r.json());
521
+
522
+ // 添加映射
523
+ const newMapping = await fetch('/api/mappings', {
524
+ method: 'POST',
525
+ headers: { 'Content-Type': 'application/json' },
526
+ body: JSON.stringify({
527
+ name: '新映射',
528
+ listen: { port: 8888 },
529
+ target: { host: '127.0.0.1', port: 80 }
530
+ })
531
+ }).then(r => r.json());
532
+ ```