# asaihost-nodejs 中文文档

规范地址：https://npmcn.com/package/asaihost-nodejs/

版本：0\.0\.35
npm 元数据更新时间：2026-11-03T03:29:53.864Z
中文译文更新时间：2026-11-03T03:56:30.304Z
软件包许可：ISC

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/asaihost-nodejs
原文对照：https://npmcn.com/package/asaihost-nodejs/?lang=original
项目主页：https://github.com/asai-cc/asaicms-v1/tree/main/webserver/src/plugs/asaihost-nodejs#readme
源码仓库：https://github.com/asai-cc/asaicms-v1#main

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：1.4 MB，52 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=20\.0\.0
- 运行依赖：1 个
- 首次发布：2026-07-03T03:04:58.599Z
- 最新版本发布：2026-11-03T03:29:53.696Z
- 已发布版本数：30（近 12 个月 30 个）

## 中文简介

AsaiWeb CMS Node.js主机插件- HTTP/WebSocket服务器，sysserver平台API， IEC 62443安全内核

## 安装

```sh
npm install asaihost-nodejs
```

也可以使用：`pnpm add asaihost-nodejs`、`yarn add asaihost-nodejs`、`bun add asaihost-nodejs`

## README

# asaihost-nodejs

Node.js HTTP/WebSocket 服务器宿主：多站点监听、静态资源、反向代理、内置 sysserver 平台 API（登录/RBAC/日志/数据库），以及 IEC 62443 安全内核。

| 项 | 值 |
| --- | --- |
| **npm 包名** | `asaihost-nodejs` |
| **插件入口** | `index.ts`（monorepo 源码）/ `dist/asaihost-nodejs.es.js`（发布后） |
| **运行时配置** | `asaihost.json` + 密钥侧车 / 环境变量 |
| **Node 版本** | `>= 20` |
| **对等依赖** | `ws ^8.18`（`mysql` / `sqlite3` 按数据存储选配） |

### 目录结构

```
asaihost-nodejs/
├── index.ts              # 包入口（默认导出 initAsaiHostNodejs + 命名 API）
├── package.json
├── tsup.config.ts
├── README.md
└── src/
    ├── index.ts          # StartAsaiHostNodejs 启动链
    ├── host-api.ts       # 对外公开 API 聚合
    ├── types.ts          # AsaiRuntime / HostConfig 等类型
    ├── core/             # init、host-config、network
    ├── http/             # server、static、security
    ├── ws/               # server、auth、message
    ├── proxy/            # 反向代理
    ├── config/           # validate、secrets
    ├── infra/            # registry、security
    ├── common/server/    # ServerApi / ServerSys / ServerWs / WsClient 基类
    └── sysserver/        # 内置 api/ws/db/lib
```

* * *

## 目录

1.  [快速开始](https://npmcn.com/package/asaihost-nodejs/#readme-%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B)
2.  [在 AsaiCMS monorepo 内使用](https://npmcn.com/package/asaihost-nodejs/#readme-%E5%9C%A8-asaicms-monorepo-%E5%86%85%E4%BD%BF%E7%94%A8)
3.  [独立项目集成](https://npmcn.com/package/asaihost-nodejs/#readme-%E7%8B%AC%E7%AB%8B%E9%A1%B9%E7%9B%AE%E9%9B%86%E6%88%90)
4.  [启动与校验流程](https://npmcn.com/package/asaihost-nodejs/#readme-%E5%90%AF%E5%8A%A8%E4%B8%8E%E6%A0%A1%E9%AA%8C%E6%B5%81%E7%A8%8B)
5.  [配置文件总览](https://npmcn.com/package/asaihost-nodejs/#readme-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E6%80%BB%E8%A7%88)
6.  [配置 → 运行时映射](https://npmcn.com/package/asaihost-nodejs/#readme-%E9%85%8D%E7%BD%AE--%E8%BF%90%E8%A1%8C%E6%97%B6%E6%98%A0%E5%B0%84)
7.  [公开 API 参考](https://npmcn.com/package/asaihost-nodejs/#readme-%E5%85%AC%E5%BC%80-api-%E5%8F%82%E8%80%83)
8.  [字段速查](https://npmcn.com/package/asaihost-nodejs/#readme-%E5%AD%97%E6%AE%B5%E9%80%9F%E6%9F%A5)
9.  [开发与发布 npm 包](https://npmcn.com/package/asaihost-nodejs/#readme-%E5%BC%80%E5%8F%91%E4%B8%8E%E5%8F%91%E5%B8%83-npm-%E5%8C%85)
10.  [约束与前端对应](https://npmcn.com/package/asaihost-nodejs/#readme-%E7%BA%A6%E6%9D%9F%E4%B8%8E%E5%89%8D%E7%AB%AF%E5%AF%B9%E5%BA%94)

* * *

## 快速开始

### 安装

```bash
npm install asaihost-nodejs ws
# 若使用 MySQL / SQLite 数据存储
npm install mysql sqlite3
```

### 最小引导（app.ts 模式）

```typescript
import fs from 'node:fs/promises';
import path from 'node:path';
import AsaiHostNodejs from 'asaihost-nodejs';

const $asai: any = {
  $lib: { /* 业务工具库 */ },
  connectionshttp: {},
  connectionsws: {},
  taskshttp: {},
  tasksws: {},
  servershttp: {},
  serversws: {},
  tm: 0,
  tmpdata: {},
};

// 1. 挂载 sysserver 平台能力（api/ws/db/lib）；sqlite/mysql 驱动由独立插件注入
import AsaiHostNodejsDbsqlite from './src/plugs/asaihost-nodejs-dbsqlite/index';
import AsaiHostNodejsDbmysql from './src/plugs/asaihost-nodejs-dbmysql/index';
AsaiHostNodejs.sysserver($asai, {
  db: { sqlite: AsaiHostNodejsDbsqlite, mysql: AsaiHostNodejsDbmysql },
});

$asai.serverRoot = process.cwd();
const cfg = JSON.parse(
  await fs.readFile(path.join($asai.serverRoot, 'websys/sys/asaihost.json'), 'utf8'),
);

// 2. 配置校验与密钥注入
const stripped = AsaiHostNodejs.stripForbiddenSecretsFromHostConfig(cfg);
if (stripped.length) {
  console.warn(`[AsaiHostNodejs] 已从 asaihost.json 剥离敏感字段: ${stripped.join(', ')}`);
}
$asai.hostconfig = AsaiHostNodejs.normalizeHostConfig(cfg);
AsaiHostNodejs.normalizeSecurityConfig($asai.hostconfig);
const errors = AsaiHostNodejs.validateHostConfig($asai.hostconfig);
if (errors.length) throw new Error(errors.join('; '));

const { secrets } = await AsaiHostNodejs.loadAsaiSecrets($asai.serverRoot);
AsaiHostNodejs.applySecretsToHostConfig($asai.hostconfig, secrets);
await AsaiHostNodejs.resolveHttpsCertFromPaths($asai.serverRoot, $asai.hostconfig);

// 3. 注册业务 work 处理器并启动
$asai.asaihost = AsaiHostNodejs;
const { sysWork } = sysMod($asai);
const { apiWork } = apiMod($asai);
const { wsWork } = wsMod($asai);
const { jsonpWork } = jsonpMod($asai);

const { StartAsaiHostNodejs } = AsaiHostNodejs.fn($asai, { sysWork, apiWork, wsWork, jsonpWork });
StartAsaiHostNodejs();
```

> 完整 monorepo 示例见仓库根目录 `webserver/app.ts`。

* * *

## 在 AsaiCMS monorepo 内使用

本仓库 `webserver` 通过**相对路径**引用插件源码，开发时无需先 `npm pack`：

```typescript
// webserver/app.ts
import AsaiHostNodejsMod from './src/plugs/asaihost-nodejs/index';
```

本地构建 npm 产物：

```bash
cd webserver/src/plugs/asaihost-nodejs
npm install
npm run build
```

构建产物位于 `dist/`：`asaihost-nodejs.es.js`（ESM）、`asaihost-nodejs.cjs`（CJS）、`index.d.ts`（类型声明）。

* * *

## 独立项目集成

### 1\. 项目结构建议

```
your-server/
├── app.ts                 # 引导入口（见上文）
├── websys/sys/
│   ├── asaihost.json      # 非敏感配置（可进 Git）
│   └── asaihost.secrets.local.json   # 本地密钥（勿提交）
├── .env                   # 生产推荐：环境变量注入密钥
├── webclient/             # 静态前端（hostdirs 指向）
└── src/work/              # 业务 api/sys/ws 模块
```

### 2\. TypeScript

```json
{
  "compilerOptions": {
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "esModuleInterop": true
  }
}
```

从 npm 安装后：

```typescript
import AsaiHostNodejs, { PLUGIN_NAME, type AsaiRuntime, type HostConfig } from 'asaihost-nodejs';
```

### 3\. 运行方式

-   开发：`tsx app.ts` 或 `ts-node --esm app.ts`
-   生产：先 `npm run build`（若打包业务代码），再 `node dist/app.js`

### 4\. 与传输层插件协作

TCP / gRPC / RS485 等传输插件通过 `$asai.asaihost` 挂载，**禁止**跨包 deep import `src/`。在 `asaihost.json` 中配置 `tcp` / `grpc` / `rs485` 块，由对应 npm 插件读取。

* * *

## 启动与校验流程

```
读取 asaihost.json
  → stripForbiddenSecretsFromHostConfig（敏感字段不得写在 JSON）
  → normalizeHostConfig + normalizeSecurityConfig
  → validateHostConfig（结构 fail-fast）
  → validateProductionSecurity（生产 IEC 62443 检查）
  → loadAsaiSecrets（.env / secrets.local.json / process.env）
  → applySecretsToHostConfig + resolveHttpsCertFromPaths
  → sysserver($asai) 已提前执行
  → fn($asai, { sysWork, apiWork, wsWork, jsonpWork })
  → StartAsaiHostNodejs()  → 多端口 HTTP/WS + 反向代理
```

### 默认导出 API 形状

```typescript
interface AsaiNodejsHostPlugin {
  readonly name: 'asaihost-nodejs';
  fn(runtime, options): { StartAsaiHostNodejs; asaiWs };
  sysserver(runtime, opt?: SysserverBootOptions): void;
  AsaiCommon: { ServerApi; ServerWs; ServerSys; WsClient };
  AsaiUtils: { deUserInfoWithAsai; wsMock };
  // …配置、路由、安全、传输桥接等（见 host-api.ts）
}
```

* * *

## 配置文件总览

本插件的配置由 **两份** 组成，启动时合并到 `$asai.hostconfig`：

| 文件 | 位置（monorepo 惯例） | 进 Git | 说明 |
| --- | --- | --- | --- |
| **asaihost.json** | `webserver/websys/sys/asaihost.json` | ✅ 是 | 结构与非敏感运维项 |
| **asaihost.secrets.local.json** | `webserver/websys/sys/` | ❌ 否 | 本地开发密钥（复制 example） |
| **.env** | `webserver/.env` | ❌ 否 | 环境变量注入（生产推荐） |

**加载优先级**（高 → 低）：`process.env` > `webserver/.env` > `asaihost.secrets.local.json` → 仅注入内存 `hostconfig`（不写回 JSON）。

敏感字段（`password` / `asaisn` / `aes.keybase64` / PEM 内联 / `sessionSecret` / `wssec[0]`）**禁止**写入 `asaihost.json`，启动时会剥离并报错。

### 欧盟合规：密钥如何保存（CRA / NIS2 / GDPR Art.32 / IEC 62443-4-2）

| 层级 | 做法 | 是否合规 |
| --- | --- | --- |
| ❌ Git / `asaihost.json` | 明文密钥进仓库 | 不合规（CRA 默认安全、CR 4.1） |
| 🟡 开发 | `asaihost.secrets.local.json` 或 `.env`（gitignore，权限 600） | 可接受（仅本机） |
| ✅ 生产 | 部署平台环境变量 / Docker secrets / K8s Secret / Vault / TPM | 推荐 |
| ✅ 严格生产 | 设 `ASAI_REQUIRE_ENV_SECRETS=1`，禁止只靠侧车文件 | NIS2 加固 |

**密钥分离（必做）**

| 变量 | 用途 | 生产要求 |
| --- | --- | --- |
| `ASAI_ASAISN` | Userinfo 编解码盐 | ≥32 随机；**不得**等于 sessionSecret |
| `ASAI_SESSION_SECRET` | 会话 HMAC | ≥32 随机；禁止用 asaisn 派生 |
| `ASAI_AES_KEYBASE64` | AES-256-GCM | Base64 解码 = 32 字节；与前端 `VITE_ASAI_AES_KEYBASE64` 一致 |
| `ASAI_HOST_PASSWORD` | bootstrap 密码 | ≥12；禁止弱口令 |

**生成随机密钥**

```bash
cd webserver
npm run secrets:gen              # 打印 .env 片段
npm run secrets:gen:write        # 写入 .env（已存在则拒绝覆盖）
npm run secrets:gen:json         # 打印 secrets.local 模板
```

落盘后建议：`chmod 600 .env`（Linux）；Windows 限制 ACL 仅服务账户可读。轮换时分别更新 `ASAI_ASAISN` / `ASAI_SESSION_SECRET`，避免一钥多用。

### 用户数据存储（`dataStores.user`）

默认 **file**（`webdata/webdb/users/users.json` + `sessions.json`）。在 `websys/sys/asaihost.json` 中修改 `dataStores.user` 即可切换，**重启 webserver 后生效**（`app.ts` 须已注入 sqlite/mysql 驱动）。

| type | 说明 | opt 主要字段 |
| --- | --- | --- |
| `file` | 默认，JSON 文件 | `dir`, `ext`, `create` |
| `sqlite` | 单文件库，表 `auth_users` / `auth_sessions` | `database`, `create` |
| `mysql` | 远程 MySQL | `host`, `user`, `password`, `database`, `create` |

```json
"dataStores": {
  "user": {
    "type": "file",
    "opt": { "dir": "webdata/webdb/users/", "ext": ".json", "create": 1 }
  }
}
```

切换 SQLite：

```json
"user": {
  "type": "sqlite",
  "opt": { "database": "./webdata/webdb/users/users.db", "create": 1 }
}
```

切换 MySQL：

```json
"user": {
  "type": "mysql",
  "opt": {
    "host": "127.0.0.1",
    "user": "root",
    "password": "",
    "database": "asai",
    "create": 1
  }
}
```

运行时可通过 `GET /api/user/login/config/` 查看 `userStore.type`（file / sqlite / mysql）。兼容旧字段 `userStore`，但推荐只维护 `dataStores.user`。

### 日志子系统已下线

`asaihost.json` 不再有 `logger` 配置。控制台级别、干预日志（CEF / 哈希链 / 5 年留存）、SIEM 令牌与 `/api/asailog/*` 均已删除。

-   `$asai.$log` 是立即返回的空函数，不读配置、不向控制台输出；
-   启动校验会丢弃历史 `logger` 字段，不再创建 `webdata/webdb/log/intervention` 目录；
-   WEB 操作记录（`security.auditLog`、接口追踪、日志只读门槛）已去掉，不再向控制台输出。

#### 清理历史遗留日志

已下线前写出的文件不会自动消失，用根目录脚本一次性回收（默认只预览，不删除）：

```bash
npm run logs:purge          # dry-run 预览清单与总大小
npm run logs:purge:apply    # 确认后真正删除
```

脚本清理 `webdata/webdb/log/{client,server,access,store,intervention,intervention-test}` 下的 `.txt/.log/.jsonl/.gz/.cef`。

### 完整配置 — asaihost.json（合成示例）

```json
{
  "website": { "title": "WEB", "ver": "1.0.0.0" },
  "dataStores": {
    "user": {
      "type": "file",
      "opt": { "dir": "webdata/webdb/users/", "ext": ".json", "create": 1 }
    }
  },
  "security": {
    "requireAuth": 0,
    "csp": 0,
    "authSkipPaths": [
      "/api/sys/errorcodes/*",
      "/api/user/login/auth/*",
      "/api/user/login/register/*",
      "/sys/info/*",
      "/jsonp/*"
    ],
    "minUserLevel": 0,
    "allowRegister": 1,
    "lockoutMaxFails": 5,
    "lockoutMinutes": 15,
    "sessionTtlHours": 24,
    "passwordMinLength": 8,
    "quota": {
      "enabled": 0,
      "maxApiConcurrent": 500,
      "maxApiPerIp": 30,
      "maxApiPerIpPerMin": 120,
      "maxWsPerIp": 5,
      "maxLoginSessions": 100,
      "maxSessionsPerUser": 3,
      "onUserSessionOverflow": "reject"
    }
  },
  "hosts": {
    "default": {
      "port": 9198,
      "httptype": "http://",
      "maxhttp": 2000,
      "maxws": 2000,
      "maxonline": 100,
      "hostdirs": ["webclient", "websys", "webdata/upload"]
    },
    "asai": {
      "ws": 1,
      "port": 9199,
      "hostdirs": ["webclient", "websys", "webdata/upload"]
    }
  },
  "proxyhosts": {
    "default": {
      "proxyport": 9298,
      "port": 9198,
      "httptype": "http://",
      "proxyhttptype": "http://"
    }
  },
  "aes": {
    "lv": 1,
    "aad": "asai aes aad",
    "keylength": 256,
    "ivlength": 12,
    "algorithm": "aes-256-gcm"
  },
  "httpscert": {
    "keyPath": "websys/sys/certs/local.key",
    "certPath": "websys/sys/certs/local.crt"
  },
  "ip": "asai",
  "index": "index.html"
}
```

> 完整字段模板见 monorepo `webserver/websys/sys/asaihost.json`；校验规则以 `validateHostConfig` 源码为准。

### 密钥侧车

复制 `websys/sys/asaihost.secrets.example.json` 为 `asaihost.secrets.local.json`：

```json
{
  "_comment": "禁止提交仓库。生产请改用环境变量或密钥托管。",
  "password": "<强密码≥12>",
  "asaisn": "<随机盐≥32，与 sessionSecret 不同>",
  "aesKeyBase64": "<与前端 VITE_ASAI_AES_KEYBASE64 一致>",
  "sessionSecret": "<随机密钥≥32，与 asaisn 不同>",
  "wssecToken": "<随机 token>"
}
```

### 环境变量（等价）

```env
ASAI_HOST_PASSWORD=
ASAI_ASAISN=
ASAI_AES_KEYBASE64=
ASAI_SESSION_SECRET=
ASAI_WSSEC_TOKEN=
ASAI_REQUIRE_ENV_SECRETS=1
ASAI_HTTPS_KEY_PATH=websys/sys/certs/local.key
ASAI_HTTPS_CERT_PATH=websys/sys/certs/local.crt
```

| 环境变量 | 注入到 hostconfig | 说明 |
| --- | --- | --- |
| `ASAI_HOST_PASSWORD` | `password` | 系统 bootstrap 密码 |
| `ASAI_ASAISN` | `asaisn` | Userinfo 编解码盐 |
| `ASAI_AES_KEYBASE64` | `aes.keybase64` | AES-256-GCM 密钥 |
| `ASAI_SESSION_SECRET` | `security.sessionSecret` | 会话 HMAC（须与 asaisn 分离） |
| `ASAI_WSSEC_TOKEN` | `wssec[0]` | WS 安全令牌 |
| `ASAI_REQUIRE_ENV_SECRETS` | （校验开关） | `1`\=生产禁止仅靠 secrets.local |

* * *

## 配置 → 运行时映射

```
asaihost.json + secrets
├── website / ip / index / mimetypes / pkg     → 全局
├── tm.*                                       → init, ws/auth, process
├── security.*                                 → access-control, user-store, quota
├── userStore.*                                → sysserver/api/login/store
├── aes.* (+ secrets.keybase64)                → API/WS 加解密
├── httpscert.*                                → https.createServer
├── hosts.default + hosts.<site>               → http/*, ws/*
│   ├── weburls.*                              → SPA fallback
│   └── ckhttp / ckws / max*                   → 连接监控与限流
├── proxyhosts.*                               → proxy/server
└── tcp|grpc|rs485                             → 传输插件（可选）
```

### 站点合并规则

-   实际站点 = `hosts.default` 浅合并 `hosts.<name>`
-   `default` 仅作模板，**不单独监听**
-   `asai` 为内置守护站点；9199 端口冲突时 `process.exit(1)`

### 请求路由

| URL | 处理器 |
| --- | --- |
| `/sys/*` | sysWork |
| `/api/*` | apiWork（前缀匹配 `hostserverapis`） |
| `/jsonp/*` | jsonpWork |
| 其它 | 静态（`hostdirs` 顺序查找） |
| WebSocket | wsWork（`msg.ty` 最长前缀） |

* * *

## 公开 API 参考

**npm 消费者仅使用包入口导出，禁止 deep import `src/`。**

### 配置与密钥

| 导出 | 说明 |
| --- | --- |
| `normalizeHostConfig` | 填充默认值、规范化结构 |
| `validateHostConfig` | 结构校验，返回错误字符串数组 |
| `validateProductionSecurity` | 生产环境 IEC 62443 检查 |
| `normalizeSecurityConfig` | 安全策略默认值 |
| `stripForbiddenSecretsFromHostConfig` | 剥离 JSON 中的敏感字段 |
| `loadAsaiSecrets` / `loadDotEnvFile` | 加载密钥 |
| `applySecretsToHostConfig` | 合并密钥到 hostconfig |
| `resolveHttpsCertFromPaths` | 从路径读取 PEM |
| `validateAsaiSecrets` / `isDevDefaultPassword` | 密钥校验 |

### 路由注册

| 导出 | 说明 |
| --- | --- |
| `registerApi` / `registerWs` | 注册业务路由工厂 |
| `initHostserverApi` / `initHostserverWs` | 初始化平台路由表 |
| `syncApiRouteKeys` / `syncWsRouteKeys` | 同步路由键列表 |

### 安全与基础设施

| 导出 | 说明 |
| --- | --- |
| `secureCompare` | 恒定时间字符串比较 |
| `resolvePathUnderRoots` / `getHostAllowedRoots` | 防目录穿越 |
| `validateHttpAuth` / `validateHttpAuthAsync` | HTTP Userinfo 鉴权（Async 版附加服务端 lv 与 session token 绑定） |
| `logSecurityEvent` / `getClientIp` | 安全审计 |
| `sanitizeString` 等 | 输入净化 |

### 传输桥接（供 TCP/gRPC/RS485 插件）

| 导出 | 说明 |
| --- | --- |
| `getTransportBlock` / `getTransportDefault` | 读取 hostconfig 传输配置 |
| `initTransportBridge` | 初始化 `$asai` 传输桥 |
| `ensureDefaultConnection` / `createTransportDataHandler` | 连接与数据回调 |

### 类型导出

`AsaiRuntime`、`HostConfig`、`AsaiHostNodejsOptions`、`HostBootResult`、`AsaiSecrets`、`SecurityConfig` 等见 `import type { … } from 'asaihost-nodejs'`。

* * *

## 字段速查

### security

| 字段 | 默认 | 说明 |
| --- | --- | --- |
| `requireAuth` | 0 | 1=API 需登录（authSkipPaths 除外） |
| `csp` | 0 | 1=启用 Content-Security-Policy |
| `quota.enabled` | 0 | 连接/频率配额 |

### hosts.default 常用

| 字段 | 默认 | 说明 |
| --- | --- | --- |
| `port` | 9198 | 监听端口 |
| `httptype` | `http://` | 或 `https://` |
| `ws` | — | 1=同端口挂载 WebSocket |
| `maxhttp` / `maxws` / `maxonline` | 2000/2000/100 | 限流 |
| `hostdirs` | — | 静态目录（按序查找） |
| `close` | 0 | 1=跳过启动 |

### aes

| 字段 | 说明 |
| --- | --- |
| `lv` | 1=可选 AES；>1=全量 AES（须 secrets 注入 keybase64） |
| `aad` / `algorithm` / `keylength` / `ivlength` | GCM 参数 |

* * *

## 开发与发布 npm 包

```bash
cd webserver/src/plugs/asaihost-nodejs
npm install
npm run typecheck    # 仅类型检查
npm run build        # tsup 打包 + tsc 声明
npm pack             # 本地验证 .tgz 内容
npm publish --access public
```

### package.json exports 约定

| 条件 | 解析到 |
| --- | --- |
| `development` | `./index.ts`（monorepo + tsx 开发） |
| `import` | `./dist/asaihost-nodejs.es.js` |
| `require` | `./dist/asaihost-nodejs.cjs` |
| `types` | `./dist/asaihost-nodejs.d.ts` |

### 注意事项

1.  修改 `asaihost.json` 后需**重启** Node 进程。
2.  端口冲突以 `listen` 的 `EADDRINUSE` 为准（启动前不再用 netstat 预告警，避免误报）；排查 9198/9199。
3.  `CONFIG_WARN` 表示使用 dev 默认密码，生产请设 `ASAI_HOST_PASSWORD`。
4.  业务处理器经模块级 `workHandlers` Map 注册，不污染 `global`。
5.  静态路径经 `resolvePathUnderRoots` 防目录穿越。

### 近期修复（鉴权 / 会话 / WS）

| 项 | 行为 |
| --- | --- |
| **WS 关闭清 session** | `removeSessionOnWsClose` 用 `Userinfo[2]`（pri）匹配会话表 `s.pri`（`deUserInfoWithAsai` 后 token 为 pri，非 pub/asencode） |
| **禁用平台用户** | `attachServerUserLevel`：平台库命中且 `status !== 'active'` 时设 `_serverUser = null`，**不再回落** channel json 伪装 active |
| **已踢 WS 不计在线** | `isUserOnlineOnWs` / `listOnlineWsEntries` 跳过 `_asaiKicked` 连接（readyState 仍短暂 OPEN 时不占配额/误拦登录） |
| **强制登录接管锁** | `/api/user/login/force/` 仅给接管方 `setForceTakeover`；被踢用户不得持锁（防重连反踢）；踢后刷新 TTL；名额仍满则再踢一轮可踢用户；`loginWs` 清锁窗口 800ms → 4s |
| **admin/quota 只读** | `/api/user/login/admin/quota/` 不再 `purgeOfflineSessions`；HTTP 刚登录尚未建 WS 的会话不会被误删 |
| **HTTP session 绑定** | `validateHttpAuthAsync`：用户已有 session 记录时校验 `Userinfo[2]` 与 `s.pri`/`s.pub` 且未过期；无 session 的历史/channel 用户保持原等级校验 |
| **clearWsServerTasks** | WS server 出错/关闭时清理定时任务，但**保留** `tasksws[hostdir].httpMonitor`，避免 HTTP 限流监控静默失效 |

* * *

## 测试

单元测试目录：`.youmengyu/test/unit/webserver/plugs/asaihost-nodejs/`

```bash
# 仓库根目录
node .youmengyu/test/asaitest.mjs --no-open

# 或单文件（现有示例）
npx tsx --test .youmengyu/test/unit/webserver/plugs/asaihost-nodejs/login/resolve-user-lv.test.mjs
npx tsx --test .youmengyu/test/unit/webserver/plugs/asaihost-nodejs/ws/auth-isOkWs.test.mjs
```

* * *

## 约束与前端对应

-   与 `asaihost-vue`（前端）配对使用时，保持 `asaihost.json` / `aes` 密钥 / `VITE_ASAI_AES_KEYBASE64` 一致。
-   插件间禁止跨包 `plugs/*/src/` 引用；消费方只走 `import from 'asaihost-nodejs'`。
-   传输层插件仅通过 `$asai.asaihost.*` 与宿主协作。

| 前端 | 后端 |
| --- | --- |
| `asaihost-vue` | `asaihost-nodejs` |
| `public/webmodel/cfg.js` | `websys/sys/asaihost.json` |
| `VITE_ASAI_AES_KEYBASE64` | `ASAI_AES_KEYBASE64` |

* * *

_配置以本 README 与 `validateHostConfig` 源码为准；monorepo 实参见 `webserver/websys/sys/asaihost.json` 与 `webserver/app.ts`。_

## 常见问题

### asaihost-nodejs 是什么？

AsaiWeb CMS Node\.js主机插件- HTTP/WebSocket服务器，sysserver平台API， IEC 62443安全内核

### 如何安装 asaihost-nodejs？

在项目目录运行 npm install asaihost-nodejs，或使用 pnpm add / yarn add / bun add。

### asaihost-nodejs 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### asaihost-nodejs 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

### asaihost-nodejs 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=20\.0\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### asaihost-nodejs 可以商用吗？

asaihost-nodejs 使用 ISC 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### asaihost-nodejs 还在维护吗？

最新版本 0\.0\.35 发布于 2026/11/03（今天）；至今共发布 30 个版本，其中过去 12 个月发布了 30 个；首次发布于 2026/07/03。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

