🥚 如何在 tegg 中集成 cnpmcore
January 6, 2026 · View on GitHub
文档中的示例项目可以在 这里 查看,在开始前请确保已阅读 DEVELOPER.md 中的相关文档,完成本地开发环境搭建。
在生产环境中,我们也可以直接部署 cnpmcore 系统,实现完整的 Registry 镜像功能。 但通常,在企业内部会有一些内部的中间件服务或限制,例如文件存储、缓存服务、登录鉴权流程等。
除了源码部署、二次开发的方式,我们还提供了 npm 包的方式,便于 tegg 应用集成。 这样既可以享受到丰富的自定义扩展能力,又可以享受到 cnpmcore 持续迭代的功能演进。
下面,让我们以 tegg 初始化的应用为例,以 npm 包的方式集成 cnpmcore,并扩展登录功能,以支持企业内 SSO 登录。
🚀 快速开始
🆕 新建一个 tegg 应用
我们以 https://github.com/eggjs/examples/tree/master/hello-tegg 为例
.
├── app
│ ├── biz
│ ├── controller
│ └── middleware
├── config
│ ├── config.default.ts
│ └── plugin.ts
├── package.json
├── test
│ ├── biz
│ └── controller
└── tsconfig.json
📦︎ 安装 cnpmcore 修改对应配置
npm i cnpmcore
- 修改
ts-config.json配置,这是因为 cnpmcore 使用了 subPath
{
"extends": "@eggjs/tsconfig",
"compilerOptions": {
"baseUrl": "./",
"target": "ES2021"
}
}
- 修改
config/plugin.ts文件,开启 cnpmcore 依赖的一些插件
import tracerPlugin from '@eggjs/tracer';
import typeboxValidatePlugin from '@eggjs/typebox-validate';
import redisPlugin from '@eggjs/redis';
// 开启如下插件
export default {
...redisPlugin(),
...tracerPlugin(),
...typeboxValidatePlugin(),
};
- 修改
config.default.ts文件,可以直接覆盖默认配置
import { SyncMode } from 'cnpmcore/common/constants';
import { cnpmcoreConfig } from 'cnpmcore/common/config';
export default () => {
const config = {};
config.cnpmcore = {
...cnpmcoreConfig,
enableChangesStream: false,
syncMode: SyncMode.all,
allowPublicRegistration: true,
// 放开注册配置
};
return config;
};
🧑🤝🧑 集成 cnpmcore
- 创建文件夹,用于存放自定义的 infra module,这里以 app/infra 为例
├── infra
│ ├── AuthAdapter.ts
│ ├── NFSAdapter.ts
│ ├── QueueAdapter.ts
│ └── package.json
- 添加
package.json,声明 infra 作为一个 eggModule 单元
{
"name": "infra",
"eggModule": {
"name": "infra"
}
}
- 添加
XXXAdapter.ts在对应的 Adapter 中继承 cnpmcore 默认的 Adapter,以 AuthAdapter 为例
import { AccessLevel, SingletonProto } from 'egg';
import { AuthAdapter } from 'cnpmcore/infra/AuthAdapter';
@SingletonProto({
name: 'authAdapter',
accessLevel: AccessLevel.PUBLIC,
})
export class MyAuthAdapter extends AuthAdapter {}
- 添加
config/module.json,将 cnpmcore 作为一个 module 集成进我们新增的 tegg 应用中
[
{
"path": "../app/biz"
},
{
"path": "../app/infra"
},
{
"package": "cnpmcore/common"
},
{
"package": "cnpmcore/core"
},
{
"package": "cnpmcore/port"
},
{
"package": "cnpmcore/repository"
}
]
✍🏻 重载 AuthAdapter 实现
我们以 AuthAdapter 为例,来实现 npm cli 的 SSO 登录的功能。
我们需要实现了 getAuthUrl 和 ensureCurrentUser 这两个方法:
- getAuthUrl 引导用户访问企业内实际的登录中心。
- ensureCurrentUser 当用户完成访问后,需要回调到应用进行鉴权流程。
我们约定通过
POST /-/v1/login/sso/:sessionId这个路由来进行登录验证。 当然,你也可以任意修改地址和登录回调,只需保证更新 redis 中的 token 状态即可。
修改 AuthAdapter.ts 文件
import { AccessLevel, Context, SingletonProto } from 'egg';
import { AuthAdapter } from 'cnpmcore/infra/AuthAdapter';
import { randomUUID } from 'crypto';
import { AuthUrlResult, userResult } from 'cnpmcore/dist/app/common/typing';
const ONE_DAY = 3600 * 24;
@SingletonProto({
name: 'authAdapter',
accessLevel: AccessLevel.PUBLIC,
})
export class MyAuthAdapter extends AuthAdapter {
async getAuthUrl(ctx: Context): Promise<AuthUrlResult> {
const sessionId = randomUUID();
await this.redis.setex(sessionId, ONE_DAY, '');
return {
// 替换实际企业内的登录中心地址,这里我们以系统内默认的 hello 路由为例
loginUrl: `${ctx.origin}/hello?name=${sessionId}`,
doneUrl: `${ctx.href}/done/session/${sessionId}`,
};
}
async ensureCurrentUser(): Promise<userResult | null> {
return {
name: 'hello',
email: 'hello@cnpmjs.org',
};
}
}
修改 HelloController 的实现,实际也可以通过登录中心回调、页面确认等方式实现
// 触发回调接口,会自动完成用户创建
await this.httpclient.request(`${ctx.origin}/-/v1/login/sso/${name}`, { method: 'POST' });
🎉 功能验证
- 在命令行输入
npm login --registry=http://127.0.0.1:7001
npm login --registry=http://127.0.0.1:7001
npm notice Log in on http://127.0.0.1:7001/
Login at:
http://127.0.0.1:7001/hello?name=e44e8c43-211a-4bcd-ae78-c4cbb1a78ae7
Press ENTER to open in the browser...
-
界面提示回车打开浏览器访问登录中心,也就是我们在 getAuthUrl,返回的 loginUrl 配置
-
由于我们 mock 了对应实现,界面会直接显示登录成功
Logged in on http://127.0.0.1:7001/.
- 在命令行输入
npm whoami --registry=http://127.0.0.1:7001验证
npm whoami --registry=http://127.0.0.1:7001
hello