asai-nodejs-host 0.2.31 → 0.2.33
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 +289 -344
- package/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,435 +2,380 @@
|
|
|
2
2
|
|
|
3
3
|
## 功能概述
|
|
4
4
|
|
|
5
|
-
`asai-nodejs-host`
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- 连接数监控与限流
|
|
15
|
-
- 优雅关闭
|
|
5
|
+
`asai-nodejs-host` 是系统的**核心服务器引擎**,基于 Node.js 原生 `http`/`https` 与 `ws` 库,负责:
|
|
6
|
+
|
|
7
|
+
- 多站点 HTTP/HTTPS 并行托管(`hosts`)
|
|
8
|
+
- 反向代理(`proxyhosts`)
|
|
9
|
+
- WebSocket 服务(鉴权、在线数限制、广播)
|
|
10
|
+
- 静态资源服务(SPA fallback、ETag、LRU 缓存)
|
|
11
|
+
- 日志、进程守护、传输层桥接(TCP/gRPC/RS485)
|
|
12
|
+
|
|
13
|
+
配置文件路径:`webserver/websys/sys/asaihost.json`(运行时挂载为 `$asai.hostconfig`)。
|
|
16
14
|
|
|
17
15
|
---
|
|
18
16
|
|
|
19
|
-
##
|
|
17
|
+
## 目录结构
|
|
20
18
|
|
|
21
19
|
```
|
|
22
20
|
asai-nodejs-host/
|
|
23
|
-
├──
|
|
24
|
-
├── AsaiHost.ts ← 入口文件
|
|
21
|
+
├── AsaiHost.ts ← npm/插件入口(fn + AsaiCommon + AsaiUtils + HostApi)
|
|
25
22
|
└── src/
|
|
26
|
-
├── index.ts
|
|
27
|
-
├── types.ts
|
|
28
|
-
├──
|
|
29
|
-
|
|
30
|
-
├──
|
|
31
|
-
├──
|
|
32
|
-
├──
|
|
33
|
-
├──
|
|
34
|
-
|
|
23
|
+
├── index.ts ← 插件工厂:注册 workHandlers、StartAsaiHost
|
|
24
|
+
├── types.ts ← TypeScript 类型
|
|
25
|
+
├── host-api.ts ← 对外公开 API(禁止 deep import src/ 内部)
|
|
26
|
+
│
|
|
27
|
+
├── core/ ← 启动与配置
|
|
28
|
+
│ ├── init.ts ← initAsaiHost(IP/日志/进程/AES/注册表)
|
|
29
|
+
│ ├── host-config.ts ← getHostCFG / getProxyHostCFG / validateHostdir
|
|
30
|
+
│ ├── network.ts ← getIp / getHostIP
|
|
31
|
+
│ ├── time.ts ← getNowTime
|
|
32
|
+
│ └── index.ts
|
|
33
|
+
│
|
|
34
|
+
├── http/ ← HTTP 层
|
|
35
|
+
│ ├── server.ts ← 路由编排、启动、asai 守护
|
|
36
|
+
│ ├── security.ts ← 安全响应头、Userinfo 解析
|
|
37
|
+
│ ├── log.ts ← 请求日志过滤(skip / sample)
|
|
38
|
+
│ ├── static.ts ← 静态查找、ETag、SPA fallback
|
|
39
|
+
│ ├── connection.ts ← 连接数监控与限流
|
|
40
|
+
│ └── static-path-cache.ts
|
|
41
|
+
│
|
|
42
|
+
├── ws/ ← WebSocket 层
|
|
43
|
+
│ ├── server.ts ← WSS 创建与 connection 分发
|
|
44
|
+
│ ├── auth.ts ← 鉴权、登录态、踢下线
|
|
45
|
+
│ ├── message.ts ← 消息序列化、sendws/broadws
|
|
46
|
+
│ └── route-index.ts ← WS 路由前缀索引
|
|
47
|
+
│
|
|
48
|
+
├── proxy/
|
|
49
|
+
│ └── server.ts ← 反向代理
|
|
50
|
+
│
|
|
51
|
+
├── config/
|
|
52
|
+
│ ├── syscom.ts ← syscom 传输配置读取与归一化
|
|
53
|
+
│ └── validate.ts ← asaihost.json 启动校验
|
|
54
|
+
│
|
|
55
|
+
├── infra/ ← 基础设施
|
|
56
|
+
│ ├── logger.ts ← 缓冲日志、按日轮转
|
|
57
|
+
│ ├── process.ts ← 异常捕获、优雅关闭
|
|
58
|
+
│ ├── registry.ts ← API/WS 路由注册
|
|
59
|
+
│ ├── transport-bridge.ts
|
|
60
|
+
│ ├── safe-path.ts ← 路径白名单校验
|
|
61
|
+
│ └── secure-compare.ts
|
|
62
|
+
│
|
|
63
|
+
├── utils/
|
|
64
|
+
│ ├── userinfo.ts ← Userinfo AES 解密
|
|
65
|
+
│ ├── ws-mock.ts ← WS Mock 工具
|
|
66
|
+
│ └── index.ts
|
|
67
|
+
│
|
|
68
|
+
└── common/ ← 路由处理器基类(Api / Sys / Ws / WsClient)
|
|
69
|
+
└── server/
|
|
35
70
|
```
|
|
36
71
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
## 入口文件 — `AsaiHost.ts`
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
module.exports = ($asai: any, opt: any) => {
|
|
43
|
-
const { StartAsaiHost, asaiWs } = AsaiHost($asai, opt);
|
|
44
|
-
return { StartAsaiHost, asaiWs };
|
|
45
|
-
};
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
**返回值**:
|
|
49
|
-
| 名称 | 类型 | 说明 |
|
|
50
|
-
|------|------|------|
|
|
51
|
-
| `StartAsaiHost` | function | 启动所有 HTTP/HTTPS/WebSocket 服务器 |
|
|
52
|
-
| `asaiWs` | object | `ws` 模块引用 |
|
|
72
|
+
> 源码仅保留 TypeScript(`tsx` 直接运行);`syscom-config.ts` 为对外 API 兼容入口,指向 `config/syscom.ts`。
|
|
53
73
|
|
|
54
74
|
---
|
|
55
75
|
|
|
56
|
-
##
|
|
76
|
+
## asaihost.json 完整配置说明
|
|
57
77
|
|
|
58
|
-
|
|
78
|
+
配置加载后赋值给 `$asai.hostconfig`。各站点实际生效配置 = `hosts.default` 与 `hosts.<站点名>` 浅合并;代理同理 = `proxyhosts.default` + `proxyhosts.<代理名>`。
|
|
59
79
|
|
|
60
|
-
|
|
61
|
-
1. 将 `opt` 中的处理器(`sysWork`, `apiWork`, `wsWork`, `jsonpWork`)挂载到 `global`
|
|
62
|
-
2. 提供 `StartAsaiHost()` 函数
|
|
80
|
+
### 顶层字段
|
|
63
81
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
82
|
+
| 字段 | 类型 | 示例 | 说明 | 读取模块 |
|
|
83
|
+
|------|------|------|------|----------|
|
|
84
|
+
| `website.title` | string | `"WEB"` | 日志文件头 Software 名称 | `infra/logger` |
|
|
85
|
+
| `website.ver` | string | `"1.0.0.0"` | 日志文件头 Version | `infra/logger` |
|
|
86
|
+
| `password` | string | `"asai"` | 系统密码(**必填**,启动校验) | `config/validate` |
|
|
87
|
+
| `ip` | string | `"asai"` | 全局绑定 IP;`"asai"`=自动探测本机 IPv4 并 localhost 监听 | `core/network` |
|
|
88
|
+
| `index` | string | `"index.html"` | 默认首页文件名 | `http/server` |
|
|
89
|
+
| `asaisn` | string | — | Userinfo 解密盐(AsCode.asdecode) | `utils/userinfo` |
|
|
90
|
+
| `wssec` | string[] | `["asai","","-"]` | WS 安全相关(业务层使用) | hostweb |
|
|
91
|
+
| `pkg.type` | number | `1` | 打包模式标识 | 业务层 |
|
|
68
92
|
|
|
69
|
-
|
|
93
|
+
### `tm` — 定时器
|
|
70
94
|
|
|
71
|
-
|
|
95
|
+
| 字段 | 默认 | 说明 |
|
|
96
|
+
|------|------|------|
|
|
97
|
+
| `tm.c` | — | 通用定时(ms) |
|
|
98
|
+
| `tm.d` | `2000` | asai 守护重启延迟 / WS 踢下线探测超时 |
|
|
99
|
+
| `tm.maxtry` | `100` | 进程异常重启上限窗口内次数 |
|
|
100
|
+
| `tm.restartwindow` | `60000` | 异常重启统计窗口(ms) |
|
|
72
101
|
|
|
73
|
-
|
|
74
|
-
type WorkHandler = (req: any, res: any, hostdir: string) => void;
|
|
75
|
-
type WsWorkHandler = (msg: any, ws: any, hostdir: string, req: any) => void;
|
|
102
|
+
### `path` — 路径
|
|
76
103
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
jsonpWork: WorkHandler; // /jsonp/* 路由处理器
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
---
|
|
104
|
+
| 字段 | 说明 |
|
|
105
|
+
|------|------|
|
|
106
|
+
| `path.mock` | Mock 数据目录 |
|
|
107
|
+
| `path.ip` | IP 相关资源目录 |
|
|
86
108
|
|
|
87
|
-
### `
|
|
109
|
+
### `zip`
|
|
88
110
|
|
|
89
|
-
|
|
|
111
|
+
| 字段 | 说明 |
|
|
90
112
|
|------|------|
|
|
91
|
-
| `
|
|
92
|
-
| `getNowTime(type, date)` | 格式化时间,支持 6 种格式 |
|
|
93
|
-
| `validateHostdir(hostdir)` | 验证主机目录名是否合法(字母数字下划线中划线) |
|
|
94
|
-
| `getHostIP(ip, $asai)` | 获取主机 IP |
|
|
95
|
-
| `getHostCFG(hostdir, $asai)` | 合并 host 配置(默认配置 + 站点配置) |
|
|
96
|
-
| `getProxyHostCFG(proxyhostdir, $asai)` | 合并代理配置 |
|
|
97
|
-
| `initAsaiHost($asai, opt)` | 初始化:挂载方法、设置端口、初始化进程/日志、配置 AES |
|
|
98
|
-
|
|
99
|
-
---
|
|
113
|
+
| `zip.zipFileNameEncoding` | 解压文件名编码,如 `"gbk"` |
|
|
100
114
|
|
|
101
|
-
### `
|
|
115
|
+
### `logger` — 日志
|
|
102
116
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
117
|
+
| 字段 | 类型 | 示例 | 说明 |
|
|
118
|
+
|------|------|------|------|
|
|
119
|
+
| `logger.lv.view` | number/bool | `2` | 控制台输出;truthy 时 WS message 也写日志 |
|
|
120
|
+
| `logger.lv.record` | number/bool | `1` | 是否写入日志文件 |
|
|
121
|
+
| `logger.path.server` | string | `"./webdata/.../server/"` | 服务端日志目录 |
|
|
122
|
+
| `logger.path.client` | string | — | 客户端日志目录 |
|
|
123
|
+
| `logger.path.maxsize` | number | `1048576` | 单文件上限(字节),超出自动轮转 |
|
|
124
|
+
| `logger.sample` | object | `{ "API": 0.15 }` | 按日志类型采样率 0~1,降低高 QPS 写盘 |
|
|
125
|
+
| `logger.skip` | string[] | `["/sys/info/metrics"]` | 静默路径;精确匹配或 `*` 前缀通配 |
|
|
108
126
|
|
|
109
|
-
|
|
127
|
+
### `guard` — 进程守护(业务层 hostweb)
|
|
110
128
|
|
|
111
|
-
|
|
|
129
|
+
| 字段 | 说明 |
|
|
112
130
|
|------|------|
|
|
113
|
-
| `
|
|
114
|
-
| `
|
|
115
|
-
| `
|
|
131
|
+
| `guard.config` | guard.json 路径 |
|
|
132
|
+
| `guard.autoStartOnRestart` | 重启后自动拉起子进程 |
|
|
133
|
+
| `guard.default.*` | 子进程默认参数(app/ispath/trymax/cfg 等) |
|
|
116
134
|
|
|
117
|
-
|
|
118
|
-
```javascript
|
|
119
|
-
{
|
|
120
|
-
lv: {
|
|
121
|
-
view: true, // 是否在控制台显示
|
|
122
|
-
record: true // 是否记录到文件
|
|
123
|
-
},
|
|
124
|
-
path: {
|
|
125
|
-
client: './logs/client/', // 客户端日志目录
|
|
126
|
-
server: './logs/server/', // 服务器日志目录
|
|
127
|
-
maxsize: 1048576 // 单个日志文件最大字节
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
```
|
|
135
|
+
### `aes` — 加密
|
|
131
136
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
137
|
+
| 字段 | 说明 |
|
|
138
|
+
|------|------|
|
|
139
|
+
| `aes.lv` | 加密等级;`>1` 时 API/WS 全量 AES |
|
|
140
|
+
| `aes.keybase64` | AES 密钥(Base64) |
|
|
141
|
+
| `aes.aad` | 附加认证数据 |
|
|
142
|
+
| `aes.keylength` | 密钥长度(bit) |
|
|
143
|
+
| `aes.ivlength` | IV 长度 |
|
|
144
|
+
| `aes.algorithm` | 如 `"aes-256-gcm"` |
|
|
136
145
|
|
|
137
|
-
|
|
146
|
+
启动时若 `aes.lv` 有效且存在 `AsAES`,挂载 `$asai.$lib.aesfns`。
|
|
138
147
|
|
|
139
|
-
### `
|
|
148
|
+
### `httpscert` — 全局 HTTPS 证书
|
|
140
149
|
|
|
141
|
-
|
|
|
142
|
-
|
|
143
|
-
| `
|
|
144
|
-
| `
|
|
145
|
-
| `SIGTERM` / `SIGINT` | 优雅关闭所有 HTTP/WS 服务器,超时 60 秒强制退出 |
|
|
150
|
+
| 字段 | 说明 |
|
|
151
|
+
|------|------|
|
|
152
|
+
| `httpscert.key` | PEM 私钥(字符串或 Buffer) |
|
|
153
|
+
| `httpscert.cert` | PEM 证书 |
|
|
146
154
|
|
|
147
|
-
|
|
148
|
-
1. 关闭所有 HTTP 服务器
|
|
149
|
-
2. 关闭所有 WebSocket 服务器
|
|
150
|
-
3. 清除守护定时器
|
|
151
|
-
4. 清除 WS 心跳监控
|
|
152
|
-
5. 3 秒后退出进程
|
|
155
|
+
站点 `httptype` 为 `https://` 时,优先用站点级 `httpscert`,否则回退全局。
|
|
153
156
|
|
|
154
|
-
|
|
157
|
+
### `mimetypes` — 全局 MIME
|
|
155
158
|
|
|
156
|
-
|
|
159
|
+
扩展名 → Content-Type 映射。站点可覆盖:`hosts.<name>.mimetypes` 与全局合并。
|
|
157
160
|
|
|
158
|
-
|
|
161
|
+
### `hosts` — HTTP/WS 站点
|
|
159
162
|
|
|
160
|
-
#### `
|
|
161
|
-
根据协议类型返回 `http` 或 `https` 模块,并注入 `AsCreateServer` 方法。
|
|
163
|
+
#### `hosts.default`(模板,不单独启动)
|
|
162
164
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
+
| 字段 | 类型 | 默认 | 说明 |
|
|
166
|
+
|------|------|------|------|
|
|
167
|
+
| `close` | 0/1 | `0` | `1` 跳过启动 |
|
|
168
|
+
| `port` | number | `9198` | 监听端口 |
|
|
169
|
+
| `httptype` | string | `"http://"` | `"http://"` 或 `"https://"` |
|
|
170
|
+
| `wstype` | string | `"ws://"` | 控制台 WS 地址展示 |
|
|
171
|
+
| `ip` | string | — | 站点级绑定 IP,覆盖全局 |
|
|
172
|
+
| `ws` | 0/1 | — | 是否挂载 WebSocket |
|
|
173
|
+
| `ckhttp` | 0/1 | `0` | 启用 HTTP 连接数监控与限流 |
|
|
174
|
+
| `ckws` | 0/1 | `0` | 启用 WS 用户鉴权与在线表 |
|
|
175
|
+
| `maxhttp` | number | `100` | HTTP 最大并发连接 |
|
|
176
|
+
| `maxws` | number | `100` | WS 最大连接数(ckws 模式下按已登录用户计) |
|
|
177
|
+
| `maxonline` | number | `100` | 最大在线用户数(ckws) |
|
|
178
|
+
| `oncors` | 0/1 | `0` | API CORS 严格模式(0=*,1=校验 cors 列表) |
|
|
179
|
+
| `cors` | string[] | `[]` | 允许的 Origin 列表 |
|
|
180
|
+
| `hostdirs` | string[] | — | 静态文件查找目录(按序) |
|
|
181
|
+
| `mimetypes` | object | — | 站点级 MIME 覆盖 |
|
|
165
182
|
|
|
166
|
-
|
|
167
|
-
| URL 前缀 | 处理器 |
|
|
168
|
-
|----------|--------|
|
|
169
|
-
| `/sys/*` | `sysWork()` |
|
|
170
|
-
| `/api/*` | `apiWork()` |
|
|
171
|
-
| `/jsonp/*` | `jsonpWork()` |
|
|
172
|
-
| 其他 | 静态文件服务 |
|
|
183
|
+
#### `hosts.<站点名>.weburls` — SPA 路由
|
|
173
184
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
- 文件流缓存 — `Cache-Control: public, max-age=3600`
|
|
185
|
+
| 字段 | 说明 |
|
|
186
|
+
|------|------|
|
|
187
|
+
| `weburls.rewrite` | `true` 启用 SPA fallback |
|
|
188
|
+
| `weburls.webdir` | 前端构建目录(如 `"webclient"`) |
|
|
189
|
+
| `weburls.keys` | URL 前缀列表,匹配则返回 `index.html` |
|
|
180
190
|
|
|
181
|
-
|
|
182
|
-
- 通过 `ckhttp` 和 `maxhttp` 配置连接数限制
|
|
183
|
-
- 当连接数达到 `80%` 时记录警告
|
|
184
|
-
- 当达到 `95%` 时强制清理空闲超过 5 分钟的连接
|
|
185
|
-
- 每 30 分钟检查一次
|
|
191
|
+
**静态查找顺序**:`hostdirs` 各目录 → 未命中且启用 rewrite → fallback 到 `webdir/index.html`。
|
|
186
192
|
|
|
187
|
-
####
|
|
188
|
-
守护机制 — 当 `asai` 端口被占用时退出进程;否则延迟 2 秒重新启动。
|
|
193
|
+
#### 特殊站点名
|
|
189
194
|
|
|
190
|
-
|
|
191
|
-
|
|
195
|
+
| 名称 | 行为 |
|
|
196
|
+
|------|------|
|
|
197
|
+
| `default` | 仅作默认模板,**不启动** |
|
|
198
|
+
| `asai` | 内置守护站点;端口冲突时进程退出或延迟重启 |
|
|
199
|
+
| 其它 | 须匹配 `^[a-zA-Z0-9_-]+$` 且长度 ≤100 |
|
|
200
|
+
|
|
201
|
+
#### 示例(节选)
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
"estunweb": {
|
|
205
|
+
"ckhttp": 1,
|
|
206
|
+
"ckws": 1,
|
|
207
|
+
"ws": 1,
|
|
208
|
+
"maxhttp": 1000,
|
|
209
|
+
"maxws": 200,
|
|
210
|
+
"maxonline": 10,
|
|
211
|
+
"weburls": {
|
|
212
|
+
"rewrite": true,
|
|
213
|
+
"webdir": "webclient",
|
|
214
|
+
"keys": ["/cocontrol", "/couser"]
|
|
215
|
+
},
|
|
216
|
+
"hostdirs": ["home", "websys/webpage", "webdata/upload"]
|
|
217
|
+
}
|
|
218
|
+
```
|
|
192
219
|
|
|
193
|
-
|
|
220
|
+
### `proxyhosts` — 反向代理
|
|
194
221
|
|
|
195
|
-
|
|
222
|
+
#### `proxyhosts.default`
|
|
196
223
|
|
|
197
|
-
|
|
224
|
+
| 字段 | 默认 | 说明 |
|
|
225
|
+
|------|------|------|
|
|
226
|
+
| `proxyhttptype` | `"http://"` | 代理监听协议 |
|
|
227
|
+
| `proxyport` | — | 代理监听端口 |
|
|
228
|
+
| `httptype` | `"http://"` | 目标协议(用于自动拼 url) |
|
|
229
|
+
| `port` | — | 目标端口 |
|
|
230
|
+
| `redirect` | `false` | `true` 时 301 重定向而非转发 |
|
|
231
|
+
| `maxSockets` | `2000` | Agent 最大 socket |
|
|
232
|
+
| `maxFreeSockets` | `200` | 空闲 socket 池上限 |
|
|
233
|
+
| `keepAlive` | `true` | HTTP Agent keepAlive |
|
|
234
|
+
| `timeout` | `60000` | 请求超时(ms) |
|
|
235
|
+
| `keepAliveMsecs` | `30000` | keepAlive 间隔 |
|
|
236
|
+
| `rejectUnauthorized` | `false` | HTTPS 目标自签名证书 |
|
|
237
|
+
| `passUserInfo` | `false` | 是否向目标转发 `Userinfo` 头 |
|
|
238
|
+
|
|
239
|
+
#### `proxyhosts.<代理名>`
|
|
240
|
+
|
|
241
|
+
| 字段 | 说明 |
|
|
242
|
+
|------|------|
|
|
243
|
+
| `url` | 目标完整 URL;空则按 `httptype`+`ip`+`port` 自动拼接 |
|
|
244
|
+
| `proxyip` | 代理绑定 IP |
|
|
245
|
+
| `proxyport` | 代理端口 |
|
|
246
|
+
| `proxyhttptype` | 代理 HTTPS 时用 `"https://"` |
|
|
198
247
|
|
|
199
|
-
|
|
200
|
-
为指定 hostdir 创建 WebSocket 服务(附加到已有的 HTTP 服务器)。
|
|
248
|
+
### `syscom` — 传输层默认连接(TCP / gRPC / RS485)
|
|
201
249
|
|
|
202
|
-
|
|
203
|
-
| type | 行为 |
|
|
204
|
-
|------|------|
|
|
205
|
-
| 0 | 仅发送给当前连接 |
|
|
206
|
-
| 1 | 发送给除当前连接外的所有人 |
|
|
207
|
-
| 2 | 发送给所有人(广播) |
|
|
250
|
+
推荐结构:`syscom.{tcp|grpc|rs485}.default`。启动时 `normalizeHostConfig` 会合并到顶层同名键,并兼容 legacy 键 `1tcp`/`serial485` 等。
|
|
208
251
|
|
|
209
|
-
|
|
252
|
+
#### 通用 `default` 字段
|
|
210
253
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
254
|
+
| 字段 | 说明 |
|
|
255
|
+
|------|------|
|
|
256
|
+
| `id` | 连接 ID(如 `testtcp`) |
|
|
257
|
+
| `label` | 显示名称 |
|
|
258
|
+
| `autoConnect` | 启动时自动连接(默认 true,显式 `false` 关闭) |
|
|
259
|
+
| `reconnect.enabled` | 是否自动重连 |
|
|
260
|
+
| `reconnect.maxRetries` | 最大重试次数(默认 60 或 `tm.maxtry`) |
|
|
261
|
+
| `reconnect.initialDelay` | 首次重连延迟(ms) |
|
|
262
|
+
| `reconnect.maxDelay` | 最大重连间隔(ms) |
|
|
263
|
+
| `reconnect.factor` | 退避倍数 |
|
|
264
|
+
|
|
265
|
+
#### TCP `default`
|
|
266
|
+
|
|
267
|
+
| 字段 | 说明 |
|
|
268
|
+
|------|------|
|
|
269
|
+
| `host` | 目标主机 |
|
|
270
|
+
| `port` | 目标端口 |
|
|
215
271
|
|
|
216
|
-
|
|
217
|
-
1. 用户名是否存在
|
|
218
|
-
2. 同一用户名是否已登录(踢下线旧连接或阻止新连接)
|
|
219
|
-
3. 同一 Token 是否已登录
|
|
220
|
-
4. 在线人数是否达到上限(`maxonline`)
|
|
272
|
+
#### gRPC `default`
|
|
221
273
|
|
|
222
|
-
|
|
223
|
-
- 通过 `maxws` 配置限制 WS 连接数(默认 100)
|
|
224
|
-
- 超过限制时发送错误消息并关闭连接(1013 状态码)
|
|
274
|
+
同 TCP:`host` + `port`(如 `50051`)。
|
|
225
275
|
|
|
226
|
-
|
|
276
|
+
#### RS485 `default`
|
|
227
277
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
| 参数 | 默认值 | 说明 |
|
|
234
|
-
|------|--------|------|
|
|
235
|
-
| `url` | — | 目标服务器 URL |
|
|
236
|
-
| `httptype` | `http://` | 代理协议类型 |
|
|
237
|
-
| `port` | — | 代理端口 |
|
|
238
|
-
| `proxyhttptype` | — | 代理服务器自身协议类型 |
|
|
239
|
-
| `proxyport` | — | 代理服务器端口 |
|
|
240
|
-
| `redirect` | false | 设为 true 时做 301 重定向而非代理 |
|
|
241
|
-
| `keepAlive` | true | 是否保持连接 |
|
|
242
|
-
| `timeout` | 60000 | 请求超时(ms) |
|
|
243
|
-
| `maxSockets` | 1000 | 最大 socket 数 |
|
|
244
|
-
|
|
245
|
-
**特性**:
|
|
246
|
-
- 支持 HTTP→HTTPS、HTTPS→HTTP、HTTPS→HTTPS 代理
|
|
247
|
-
- 请求头过滤(去掉 transfer-encoding、connection 等转发无关头)
|
|
248
|
-
- 错误处理:502 Bad Gateway、504 Gateway Timeout
|
|
249
|
-
- 用户信息透传(在 `Userinfo` 头中传递)
|
|
278
|
+
| 字段 | 说明 |
|
|
279
|
+
|------|------|
|
|
280
|
+
| `path` | 串口路径(Windows `COM3`,Linux `/dev/ttyUSB0`) |
|
|
281
|
+
| `baudRate` | 波特率 |
|
|
282
|
+
| `dataBits` / `stopBits` / `parity` | 串口参数 |
|
|
250
283
|
|
|
251
284
|
---
|
|
252
285
|
|
|
253
|
-
|
|
286
|
+
## 请求路由
|
|
254
287
|
|
|
255
|
-
|
|
|
256
|
-
|
|
257
|
-
|
|
|
258
|
-
| `
|
|
288
|
+
| URL 前缀 | 处理器 | 注册来源 |
|
|
289
|
+
|----------|--------|----------|
|
|
290
|
+
| `/sys/*` | `sysWork` | hostweb 注入 |
|
|
291
|
+
| `/api/*` | `apiWork` | hostweb 注入 |
|
|
292
|
+
| `/jsonp/*` | `jsonpWork` | hostweb 注入 |
|
|
293
|
+
| 其它 | 静态文件 | `http/static` |
|
|
259
294
|
|
|
260
|
-
|
|
261
|
-
```
|
|
262
|
-
[decode(用户名, token), decode(等级, token), token本身]
|
|
263
|
-
```
|
|
264
|
-
使用 `$asai.$lib.AsCode.asdecode()` 进行解码。
|
|
295
|
+
WebSocket 消息 → `wsWork`(hostweb 注入)。
|
|
265
296
|
|
|
266
297
|
---
|
|
267
298
|
|
|
268
|
-
##
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
logger: {
|
|
286
|
-
lv: { view: true, record: true },
|
|
287
|
-
path: { client: './logs/', server: './logs/', maxsize: 1048576 }
|
|
288
|
-
},
|
|
289
|
-
|
|
290
|
-
// 站点配置
|
|
291
|
-
hosts: {
|
|
292
|
-
default: { // 默认配置
|
|
293
|
-
httptype: 'http://',
|
|
294
|
-
port: 80,
|
|
295
|
-
hostdirs: [], // 静态文件目录列表
|
|
296
|
-
ws: true, // 是否启用 WebSocket
|
|
297
|
-
ckws: true, // 是否鉴权
|
|
298
|
-
cors: ['*'], // 允许的跨域来源
|
|
299
|
-
maxws: 100, // 最大 WS 连接数
|
|
300
|
-
maxonline: 100, // 最大在线人数
|
|
301
|
-
maxhttp: 100 // 最大 HTTP 连接数
|
|
302
|
-
},
|
|
303
|
-
mysite: { // 具体站点
|
|
304
|
-
httptype: 'https://',
|
|
305
|
-
port: 443,
|
|
306
|
-
weburls: { // SPA URL 重写
|
|
307
|
-
rewrite: true,
|
|
308
|
-
webdir: './dist',
|
|
309
|
-
keys: ['/app', '/user']
|
|
310
|
-
},
|
|
311
|
-
httpscert: { // HTTPS 证书
|
|
312
|
-
key: fs.readFileSync('./cert/key.pem'),
|
|
313
|
-
cert: fs.readFileSync('./cert/cert.pem')
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
},
|
|
317
|
-
|
|
318
|
-
// 代理配置
|
|
319
|
-
proxyhosts: {
|
|
320
|
-
api: {
|
|
321
|
-
url: 'http://localhost:3000',
|
|
322
|
-
proxyport: 8080
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
```
|
|
299
|
+
## 模块职责速查
|
|
300
|
+
|
|
301
|
+
| 模块 | 核心导出 | 职责 |
|
|
302
|
+
|------|----------|------|
|
|
303
|
+
| `core/init` | `initAsaiHost` | 启动期挂载 $asai 方法、日志、进程、AES |
|
|
304
|
+
| `core/host-config` | `getHostCFG` | 合并并缓存 hosts/proxyhosts |
|
|
305
|
+
| `http/server` | `startHTTPServer` | HTTP 创建、路由、监听、asaiGuard |
|
|
306
|
+
| `http/static` | `createStaticHandler` | 静态文件 + SPA + ETag |
|
|
307
|
+
| `http/connection` | `setupHttpConnectionMonitor` | 80%/95% 阈值、空闲清理 |
|
|
308
|
+
| `ws/server` | `createWSHost` | WSS 生命周期 |
|
|
309
|
+
| `ws/auth` | `isOkWs`, `loginWs` | 用户名/Token/在线数鉴权 |
|
|
310
|
+
| `proxy/server` | `startProxyServer` | 反向代理、hop-by-hop 头过滤 |
|
|
311
|
+
| `config/validate` | `validateHostConfig` | 启动前 fail-fast 校验 |
|
|
312
|
+
| `config/syscom` | `normalizeHostConfig` | syscom 归一化 |
|
|
313
|
+
| `infra/logger` | `initLog` | `$asai.$log`、缓冲写盘 |
|
|
314
|
+
| `infra/process` | `gracefulShutdown` | SIGTERM/SIGINT 优雅关闭 |
|
|
315
|
+
| `infra/registry` | `registerApi/Ws` | 路由注册双写 $lib 与 hostserver* |
|
|
327
316
|
|
|
328
317
|
---
|
|
329
318
|
|
|
330
319
|
## 使用示例
|
|
331
320
|
|
|
332
321
|
```javascript
|
|
333
|
-
|
|
322
|
+
import AsaiHost from './AsaiHost';
|
|
334
323
|
|
|
335
|
-
const { StartAsaiHost
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
324
|
+
const { StartAsaiHost } = AsaiHost.fn($asai, {
|
|
325
|
+
sysWork: (req, res, hostdir) => { /* /sys */ },
|
|
326
|
+
apiWork: (req, res, hostdir) => { /* /api */ },
|
|
327
|
+
wsWork: (msg, ws, hostdir, req) => { /* WS */ },
|
|
328
|
+
jsonpWork: (req, res, hostdir) => { /* /jsonp */ },
|
|
340
329
|
});
|
|
341
330
|
|
|
342
|
-
StartAsaiHost();
|
|
331
|
+
StartAsaiHost();
|
|
343
332
|
```
|
|
344
333
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
## 注意事项
|
|
348
|
-
|
|
349
|
-
1. 所有处理器(`sysWork`/`apiWork`/`wsWork`/`jsonpWork`)在 `index.ts` 中被挂载到 `global`
|
|
350
|
-
2. `validateHostdir()` 拒绝名称为 `default` 和 `asai` 的站点
|
|
351
|
-
3. `http-server.ts` 中的 `checkFileExists()` 同时检查文件权限
|
|
352
|
-
4. 代理服务器中包含 SSL 错误处理(`rejectUnauthorized: false` 用于自签名证书)
|
|
353
|
-
5. 日志系统使用缓冲区异步写入,不会阻塞主线程
|
|
354
|
-
|
|
355
|
-
---
|
|
356
|
-
|
|
357
|
-
## 代码分析与优化建议
|
|
358
|
-
|
|
359
|
-
### ✅ 已优化项目
|
|
360
|
-
|
|
361
|
-
#### 1. 全局变量污染 ➔ 模块级 Map ✅
|
|
334
|
+
启动校验:
|
|
362
335
|
|
|
363
|
-
|
|
336
|
+
```javascript
|
|
337
|
+
import { validateHostConfig, normalizeHostConfig } from './src/host-api';
|
|
364
338
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
export function getWorkHandler(name: string): Function | undefined {
|
|
369
|
-
return workHandlers.get(name);
|
|
370
|
-
}
|
|
339
|
+
const cfg = normalizeHostConfig(JSON.parse(fs.readFileSync('asaihost.json', 'utf8')));
|
|
340
|
+
const errors = validateHostConfig(cfg);
|
|
341
|
+
if (errors.length) throw new Error(errors.join('\n'));
|
|
371
342
|
```
|
|
372
343
|
|
|
373
|
-
**优化收益**:避免全局命名冲突,支持多实例并行运行,单元测试可独立 mock。
|
|
374
|
-
|
|
375
|
-
---
|
|
376
|
-
|
|
377
|
-
#### 2. 静态文件服务增加 ETag 和 Last-Modified ✅
|
|
378
|
-
|
|
379
|
-
**优化内容**:新增 `serveStaticFile()` 函数,增加 `ETag` 和 `Last-Modified` 响应头,支持客户端条件请求(`If-None-Match` / `If-Modified-Since`),返回 304 Not Modified 减少不必要的文件传输。
|
|
380
|
-
|
|
381
|
-
**优化收益**:节省带宽约 30-50%。
|
|
382
|
-
|
|
383
|
-
---
|
|
384
|
-
|
|
385
|
-
#### 3. URL 解析增加编码保护 ✅
|
|
386
|
-
|
|
387
|
-
**优化内容**:`parseUrlPath()` 在解析前对 URL 进行 `encodeURI()` 编码,避免中文等特殊字符导致 `new URL()` 抛出异常。
|
|
388
|
-
|
|
389
|
-
---
|
|
390
|
-
|
|
391
|
-
#### 4. 日志缓冲区增加进程退出刷新 ✅
|
|
392
|
-
|
|
393
|
-
**优化内容**:`LogBuffer` 构造函数和 `initLog()` 中注册 `process.once('beforeExit')` 钩子,确保进程退出前刷新所有日志缓冲区,防止日志数据丢失。
|
|
394
|
-
|
|
395
344
|
---
|
|
396
345
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
**优化内容**:`isOkWs()` 中所有 `ws.send(msg)` 改为在回调中执行 `ws.close()`,确保消息发送完毕后再关闭连接。
|
|
400
|
-
|
|
401
|
-
---
|
|
402
|
-
|
|
403
|
-
#### 6. 代理服务器 Userinfo 透传增加开关 ✅
|
|
404
|
-
|
|
405
|
-
**优化内容**:在 `startProxyServer()` 中增加 `passUserInfo` 配置开关(默认关闭),避免用户信息泄露给第三方后端。
|
|
406
|
-
|
|
407
|
-
---
|
|
408
|
-
|
|
409
|
-
#### 7. HTTP 连接监控间隔缩短 ✅
|
|
410
|
-
|
|
411
|
-
**优化内容**:连接监控间隔从 30 分钟缩短为 5 分钟,更及时地处理连接数激增。
|
|
412
|
-
|
|
413
|
-
---
|
|
414
|
-
|
|
415
|
-
#### 8. 代码可维护性改进 ✅
|
|
346
|
+
## 注意事项
|
|
416
347
|
|
|
417
|
-
|
|
348
|
+
1. 处理器通过模块级 `workHandlers` Map 注册,**不污染 global**。
|
|
349
|
+
2. `validateHostdir()` 拒绝 `default` / `asai` 作为普通站点名启动。
|
|
350
|
+
3. 静态路径经 `resolvePathUnderRoots` 白名单校验,拒绝目录穿越;代理默认**不透传** Userinfo。
|
|
351
|
+
4. HTTP 连接监控间隔 **5 分钟**;超 95% 清理空闲 5 分钟以上连接。
|
|
352
|
+
5. 日志异步缓冲写入,进程退出时 `beforeExit` 刷盘。
|
|
418
353
|
|
|
419
354
|
---
|
|
420
355
|
|
|
421
|
-
|
|
356
|
+
## 配置 → 代码映射图
|
|
422
357
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
358
|
+
```
|
|
359
|
+
asaihost.json
|
|
360
|
+
├── website/password/ip/index/asaisn/aes/httpscert/mimetypes → 全局
|
|
361
|
+
├── logger.* → infra/logger, http/log
|
|
362
|
+
├── tm.* → core/init, ws/auth, infra/process
|
|
363
|
+
├── hosts.default + hosts.<site> → core/host-config → http/*, ws/*
|
|
364
|
+
│ ├── weburls.* → http/static (SPA)
|
|
365
|
+
│ ├── ckhttp/maxhttp → http/connection
|
|
366
|
+
│ └── ckws/maxws/maxonline → ws/auth
|
|
367
|
+
├── proxyhosts.default + proxyhosts.<name> → proxy/server
|
|
368
|
+
└── syscom.{tcp|grpc|rs485} → config/syscom → infra/transport-bridge
|
|
369
|
+
```
|
|
426
370
|
|
|
427
371
|
---
|
|
428
372
|
|
|
429
|
-
|
|
373
|
+
## 版本与优化记录
|
|
430
374
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
375
|
+
- ✅ 模块级 workHandlers(替代 global)
|
|
376
|
+
- ✅ 静态 ETag / 304、LRU 路径缓存
|
|
377
|
+
- ✅ URL encodeURI 保护、日志 exit 刷盘
|
|
378
|
+
- ✅ WS send→close 竞争修复、代理 passUserInfo 开关
|
|
379
|
+
- ✅ HTTP 模块职责拆分(core/http/ws/proxy/config/infra/utils)
|
|
380
|
+
- ✅ 删除冗余 .js 编译产物与 re-export shim;LRU O(1);日志 skip 启动期编译
|
|
381
|
+
- ✅ 静态资源 safe-path 防穿越;HTTPS 站点 HSTS 响应头
|