npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

asaihost-nodejs

v0.0.29

Published

AsaiWeb CMS Node.js host plugin — HTTP/WebSocket server, sysserver platform API, IEC 62443 security kernel

Downloads

673

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.18mysql / 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/            # logger、registry、security
    ├── common/server/    # ServerApi / ServerSys / ServerWs / WsClient 基类
    └── sysserver/        # 内置 api/ws/db/lib

目录

  1. 快速开始
  2. 在 AsaiCMS monorepo 内使用
  3. 独立项目集成
  4. 启动与校验流程
  5. 配置文件总览
  6. 配置 → 运行时映射
  7. 公开 API 参考
  8. 字段速查
  9. 开发与发布 npm 包
  10. 约束与前端对应

快速开始

安装

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

最小引导(app.ts 模式)

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

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

本地构建 npm 产物:

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

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

从 npm 安装后:

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

3. 运行方式

  • 开发:tsx app.tsts-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 形状

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;禁止弱口令 |

生成随机密钥

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

默认 filewebdata/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 |

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

切换 SQLite:

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

切换 MySQL:

"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(合成示例)

{
  "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,
    "auditLog": 1,
    "authSkipPaths": [
      "/api/asailog/*",
      "/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"
    }
  },
  "logger": {
    "lv": { "view": 2, "record": 1 },
    "path": {
      "maxsize": 1048576,
      "client": "./webdata/webdb/log/client/",
      "server": "./webdata/webdb/log/server/"
    },
    "intervention": {
      "path": "./webdata/webdb/log/intervention/",
      "retentionYears": 5
    },
    "sample": { "API": 0.15, "WS": 0.08, "REQ_OTHER": 0.2 },
    "skip": ["/sys/info/metrics", "/sys/guard/msg", "/api/asailog/*"]
  },
  "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.jsonasaihost.secrets.local.json

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

环境变量(等价)

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     → 全局
├── logger.*                                   → infra/logger, http/log
├── 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 | 连接与数据回调 |

类型导出

AsaiRuntimeHostConfigAsaiHostNodejsOptionsHostBootResultAsaiSecretsSecurityConfig 等见 import type { … } from 'asaihost-nodejs'


字段速查

security

| 字段 | 默认 | 说明 | |------|------|------| | requireAuth | 0 | 1=API 需登录(authSkipPaths 除外) | | csp | 0 | 1=启用 Content-Security-Policy | | auditLog | 1 | 安全审计日志 | | 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 包

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. 端口冲突以 listenEADDRINUSE 为准(启动前不再用 netstat 预告警,避免误报);排查 9198/9199。
  3. CONFIG_WARN 表示使用 dev 默认密码,生产请设 ASAI_HOST_PASSWORD
  4. 业务处理器经模块级 workHandlers Map 注册,不污染 global
  5. 静态路径经 resolvePathUnderRoots 防目录穿越。

近期修复(鉴权 / 会话 / WS)

| 项 | 行为 | |----|------| | WS 关闭清 session | removeSessionOnWsCloseUserinfo[2](pri)匹配会话表 s.prideUserInfoWithAsai 后 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/

# 仓库根目录
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.jsonwebserver/app.ts