feat: support Dockerfile and S3 nfs (#509)
closes https://github.com/cnpm/cnpmcore/issues/507
This commit is contained in:
6
.dockerignore
Normal file
6
.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
||||
logs
|
||||
node_modules
|
||||
run
|
||||
typings
|
||||
.cnpmcore*
|
||||
coverage
|
||||
19
Dockerfile
Normal file
19
Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
# https://stackoverflow.com/questions/65612411/forcing-docker-to-use-linux-amd64-platform-by-default-on-macos/69636473#69636473
|
||||
FROM node:18
|
||||
|
||||
# Create app directory
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
COPY . .
|
||||
|
||||
RUN npm config set registry https://registry.npmmirror.com \
|
||||
&& npm install -g npminstall \
|
||||
&& npmupdate -c \
|
||||
&& npm run tsc
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV EGG_SERVER_ENV=prod
|
||||
|
||||
EXPOSE 7001
|
||||
CMD ["npm", "run", "start:foreground"]
|
||||
30
INTEGRATE.md
30
INTEGRATE.md
@@ -1,4 +1,5 @@
|
||||
# 🥚 如何在 [tegg](https://github.com/eggjs/tegg) 中集成 cnpmcore
|
||||
|
||||
> 文档中的示例项目可以在 [这里](https://github.com/eggjs/examples/commit/bed580fe053ae573f8b63f6788002ff9c6e7a142) 查看,在开始前请确保已阅读 [DEVELOPER.md](DEVELOPER.md) 中的相关文档,完成本地开发环境搭建。
|
||||
|
||||
在生产环境中,我们也可以直接部署 cnpmcore 系统,实现完整的 Registry 镜像功能。
|
||||
@@ -12,7 +13,8 @@
|
||||
## 🚀 快速开始
|
||||
|
||||
### 🆕 新建一个 tegg 应用
|
||||
> 我们以 https://github.com/eggjs/examples/tree/master/hello-tegg 为例
|
||||
|
||||
> 我们以 <https://github.com/eggjs/examples/tree/master/hello-tegg> 为例
|
||||
|
||||
```shell
|
||||
.
|
||||
@@ -37,6 +39,7 @@
|
||||
```
|
||||
|
||||
1. 修改 `ts-config.json` 配置,这是因为 cnpmcore 使用了 [subPath](https://nodejs.org/api/packages.html#subpath-exports)
|
||||
|
||||
```json
|
||||
{
|
||||
"extends": "@eggjs/tsconfig",
|
||||
@@ -50,6 +53,7 @@
|
||||
```
|
||||
|
||||
2. 修改 `config/plugin.ts` 文件,开启 cnpmcore 依赖的一些插件
|
||||
|
||||
```typescript
|
||||
// 开启如下插件
|
||||
{
|
||||
@@ -77,6 +81,7 @@
|
||||
```
|
||||
|
||||
3. 修改 `config.default.ts` 文件,可以直接覆盖默认配置
|
||||
|
||||
```typescript
|
||||
import { SyncMode } from 'cnpmcore/common/constants';
|
||||
import { cnpmcoreConfig } from 'cnpmcore/common/config';
|
||||
@@ -104,7 +109,7 @@ export default () => {
|
||||
│ └── package.json
|
||||
```
|
||||
|
||||
* 添加 `package.json` ,声明 infra 作为一个 eggModule 单元
|
||||
* 添加 `package.json` ,声明 infra 作为一个 eggModule 单元
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -115,7 +120,7 @@ export default () => {
|
||||
}
|
||||
```
|
||||
|
||||
* 添加 `XXXAdapter.ts` 在对应的 Adapter 中继承 cnpmcore 默认的 Adapter,以 AuthAdapter 为例
|
||||
* 添加 `XXXAdapter.ts` 在对应的 Adapter 中继承 cnpmcore 默认的 Adapter,以 AuthAdapter 为例
|
||||
|
||||
```typescript
|
||||
import { AccessLevel, SingletonProto } from '@eggjs/tegg';
|
||||
@@ -159,12 +164,14 @@ export default () => {
|
||||
我们以 AuthAdapter 为例,来实现 npm cli 的 SSO 登录的功能。
|
||||
|
||||
我们需要实现了 getAuthUrl 和 ensureCurrentUser 这两个方法:
|
||||
|
||||
1. getAuthUrl 引导用户访问企业内实际的登录中心。
|
||||
2. ensureCurrentUser 当用户完成访问后,需要回调到应用进行鉴权流程。
|
||||
我们约定通过 `POST /-/v1/login/sso/:sessionId` 这个路由来进行登录验证。
|
||||
当然,你也可以任意修改地址和登录回调,只需保证更新 redis 中的 token 状态即可。
|
||||
|
||||
修改 AuthAdapter.ts 文件
|
||||
|
||||
```typescript
|
||||
import { AccessLevel, EggContext, SingletonProto } from '@eggjs/tegg';
|
||||
import { AuthAdapter } from 'cnpmcore/infra/AuthAdapter';
|
||||
@@ -199,6 +206,7 @@ export class MyAuthAdapter extends AuthAdapter {
|
||||
```
|
||||
|
||||
修改 HelloController 的实现,实际也可以通过登录中心回调、页面确认等方式实现
|
||||
|
||||
```typescript
|
||||
// 触发回调接口,会自动完成用户创建
|
||||
await this.httpclient.request(`${ctx.origin}/-/v1/login/sso/${name}`, { method: 'POST' });
|
||||
@@ -209,22 +217,24 @@ export class MyAuthAdapter extends AuthAdapter {
|
||||
1. 在命令行输入 `npm login --registry=http://127.0.0.1:7001`
|
||||
|
||||
```shell
|
||||
$ 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...
|
||||
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...
|
||||
```
|
||||
|
||||
2. 界面提示回车打开浏览器访问登录中心,也就是我们在 getAuthUrl,返回的 loginUrl 配置
|
||||
|
||||
3. 由于我们 mock 了对应实现,界面会直接显示登录成功
|
||||
|
||||
```shell
|
||||
Logged in on http://127.0.0.1:7001/.
|
||||
```
|
||||
|
||||
4. 在命令行输入 `npm whoami --registry=http://127.0.0.1:7001` 验证
|
||||
|
||||
```shell
|
||||
$ npm whoami --registry=http://127.0.0.1:7001
|
||||
$ hello
|
||||
npm whoami --registry=http://127.0.0.1:7001
|
||||
hello
|
||||
```
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import assert from 'assert';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { join } from 'path';
|
||||
import { EggAppConfig, PowerPartial } from 'egg';
|
||||
import OSSClient from 'oss-cnpm';
|
||||
@@ -31,7 +32,7 @@ export const cnpmcoreConfig: CnpmcoreConfig = {
|
||||
checkChangesStreamInterval: 500,
|
||||
changesStreamRegistry: 'https://replicate.npmjs.com',
|
||||
changesStreamRegistryMode: ChangesStreamMode.streaming,
|
||||
registry: 'http://localhost:7001',
|
||||
registry: process.env.CNPMCORE_CONFIG_REGISTRY || 'http://localhost:7001',
|
||||
alwaysAuth: false,
|
||||
allowScopes: [
|
||||
'@cnpm',
|
||||
@@ -43,7 +44,7 @@ export const cnpmcoreConfig: CnpmcoreConfig = {
|
||||
admins: {
|
||||
cnpmcore_admin: 'admin@cnpmjs.org',
|
||||
},
|
||||
enableWebAuthn: false,
|
||||
enableWebAuthn: !!process.env.CNPMCORE_CONFIG_ENABLE_WEB_AUTHN,
|
||||
enableCDN: false,
|
||||
cdnCacheControlHeader: 'public, max-age=300',
|
||||
cdnVaryHeader: 'Accept, Accept-Encoding',
|
||||
@@ -58,6 +59,7 @@ export const cnpmcoreConfig: CnpmcoreConfig = {
|
||||
export default (appInfo: EggAppConfig) => {
|
||||
const config = {} as PowerPartial<EggAppConfig>;
|
||||
|
||||
config.keys = process.env.CNPMCORE_EGG_KEYS || randomUUID();
|
||||
config.cnpmcore = cnpmcoreConfig;
|
||||
|
||||
// override config from framework / plugin
|
||||
@@ -118,6 +120,24 @@ export default (appInfo: EggAppConfig) => {
|
||||
'Cache-Control': 'max-age=0, s-maxage=60',
|
||||
},
|
||||
});
|
||||
} else if (process.env.CNPMCORE_NFS_TYPE === 's3') {
|
||||
assert(process.env.CNPMCORE_NFS_S3_CLIENT_ENDPOINT, 'require env CNPMCORE_NFS_S3_CLIENT_ENDPOINT');
|
||||
assert(process.env.CNPMCORE_NFS_S3_CLIENT_ID, 'require env CNPMCORE_NFS_S3_CLIENT_ID');
|
||||
assert(process.env.CNPMCORE_NFS_S3_CLIENT_SECRET, 'require env CNPMCORE_NFS_S3_CLIENT_SECRET');
|
||||
assert(process.env.CNPMCORE_NFS_S3_CLIENT_BUCKET, 'require env CNPMCORE_NFS_S3_CLIENT_BUCKET');
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const S3Client = require('s3-cnpmcore');
|
||||
config.nfs.client = new S3Client({
|
||||
region: process.env.CNPMCORE_NFS_S3_CLIENT_REGION || 'default',
|
||||
endpoint: process.env.CNPMCORE_NFS_S3_CLIENT_ENDPOINT,
|
||||
credentials: {
|
||||
accessKeyId: process.env.CNPMCORE_NFS_S3_CLIENT_ID,
|
||||
secretAccessKey: process.env.CNPMCORE_NFS_S3_CLIENT_SECRET,
|
||||
},
|
||||
bucket: process.env.CNPMCORE_NFS_S3_CLIENT_BUCKET,
|
||||
forcePathStyle: !!process.env.CNPMCORE_NFS_S3_CLIENT_FORCE_PATH_STYLE,
|
||||
disableURL: !!process.env.CNPMCORE_NFS_S3_CLIENT_DISABLE_URL,
|
||||
});
|
||||
}
|
||||
|
||||
config.logger = {
|
||||
|
||||
175
docs/deploy-in-docker.md
Normal file
175
docs/deploy-in-docker.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# 通过 Docker 部署 cnpmcore
|
||||
|
||||
## 构建镜像
|
||||
|
||||
```bash
|
||||
docker build -t cnpmcore .
|
||||
```
|
||||
|
||||
## 通过环境变量配置参数
|
||||
|
||||
需要在 docker 容器中配置数据存储参数,否则启动会失败,cnpmcore 镜像要求数据存储与计算分离。
|
||||
|
||||
### MySQL
|
||||
|
||||
```bash
|
||||
CNPMCORE_MYSQL_DATABASE=cnpmcore
|
||||
CNPMCORE_MYSQL_HOST=127.0.0.1
|
||||
CNPMCORE_MYSQL_PORT=3306
|
||||
CNPMCORE_MYSQL_USER=your-db-user-name
|
||||
CNPMCORE_MYSQL_PASSWORD=your-db-user-password
|
||||
```
|
||||
|
||||
### Redis
|
||||
|
||||
```bash
|
||||
CNPMCORE_REDIS_HOST=127.0.0.1
|
||||
CNPMCORE_REDIS_PORT=6379
|
||||
CNPMCORE_REDIS_PASSWORD=your-redis-password
|
||||
CNPMCORE_REDIS_DB=1
|
||||
```
|
||||
|
||||
### 文件存储
|
||||
|
||||
目前支持的文件存储服务有阿里云 OSS、AWS S3,以及兼容 S3 的 minio。
|
||||
|
||||
#### OSS
|
||||
|
||||
```bash
|
||||
CNPMCORE_NFS_TYPE=oss
|
||||
CNPMCORE_NFS_OSS_ENDPOINT==https://your-oss-endpoint
|
||||
CNPMCORE_NFS_OSS_BUCKET=your-bucket-name
|
||||
CNPMCORE_NFS_OSS_ID=oss-ak
|
||||
CNPMCORE_NFS_OSS_SECRET=oss-sk
|
||||
```
|
||||
|
||||
#### S3 / minio
|
||||
|
||||
```bash
|
||||
CNPMCORE_NFS_TYPE=s3
|
||||
CNPMCORE_NFS_S3_CLIENT_ENDPOINT=https://your-s3-endpoint
|
||||
CNPMCORE_NFS_S3_CLIENT_BUCKET=your-bucket-name
|
||||
CNPMCORE_NFS_S3_CLIENT_ID=s3-ak
|
||||
CNPMCORE_NFS_S3_CLIENT_SECRET=s3-sk
|
||||
CNPMCORE_NFS_S3_CLIENT_DISABLE_URL=true
|
||||
```
|
||||
|
||||
如果使用的是 minio,请务必设置 `CNPMCORE_NFS_S3_CLIENT_FORCE_PATH_STYLE=true`
|
||||
|
||||
```bash
|
||||
CNPMCORE_NFS_S3_CLIENT_FORCE_PATH_STYLE=true
|
||||
```
|
||||
|
||||
### 日志
|
||||
|
||||
```bash
|
||||
CNPMCORE_LOG_DIR=/var/log/cnpmcore
|
||||
```
|
||||
|
||||
### registry 域名
|
||||
|
||||
```bash
|
||||
CNPMCORE_CONFIG_REGISTRY=https://your-registry.com
|
||||
```
|
||||
|
||||
### 使用 `config.prod.js` 覆盖
|
||||
|
||||
直接覆盖 `/usr/src/app/config/config.prod.js` 文件也可以实现生产配置自定义。
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
cnpmcore: {
|
||||
registry: 'https://your-registry.com',
|
||||
enableWebAuthn: true,
|
||||
},
|
||||
orm: {
|
||||
database: 'cnpmcore',
|
||||
host: '127.0.0.1',
|
||||
port: 3306,
|
||||
user: 'your-db-user-name',
|
||||
password: 'your-db-user-password',
|
||||
},
|
||||
redis: {
|
||||
client: {
|
||||
port: 6379,
|
||||
host: '127.0.0.1',
|
||||
password: 'your-redis-password',
|
||||
db: 1,
|
||||
},
|
||||
},
|
||||
nfs: {
|
||||
client: new (require('s3-cnpmcore'))({
|
||||
region: 'default',
|
||||
endpoint: 'https://your-s3-endpoint',
|
||||
credentials: {
|
||||
accessKeyId: 's3-ak',
|
||||
secretAccessKey: 's3-sk',
|
||||
},
|
||||
bucket: 'your-bucket-name',
|
||||
forcePathStyle: true,
|
||||
disableURL: true,
|
||||
}),
|
||||
},
|
||||
logger: {
|
||||
dir: '/var/log/cnpmcore',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
通过 docker volumes 设置配置文件
|
||||
|
||||
```bash
|
||||
docker run -p 7001:7001 -it --rm \
|
||||
-v /path-to/config.prod.js:/usr/src/app/config/config.prod.js \
|
||||
--name cnpmcore-prod cnpmcore
|
||||
```
|
||||
|
||||
## 运行容器
|
||||
|
||||
```bash
|
||||
docker run -p 7001:7001 -it --rm \
|
||||
-e CNPMCORE_CONFIG_REGISTRY=https://your-registry.com \
|
||||
-e CNPMCORE_MYSQL_DATABASE=cnpmcore \
|
||||
-e CNPMCORE_MYSQL_HOST=127.0.0.1 \
|
||||
-e CNPMCORE_MYSQL_PORT=3306 \
|
||||
-e CNPMCORE_MYSQL_USER=your-db-user-name \
|
||||
-e CNPMCORE_MYSQL_PASSWORD=your-db-user-password \
|
||||
-e CNPMCORE_NFS_TYPE=s3 \
|
||||
-e CNPMCORE_NFS_S3_CLIENT_ENDPOINT=https://your-s3-endpoint \
|
||||
-e CNPMCORE_NFS_S3_CLIENT_BUCKET=your-bucket-name \
|
||||
-e CNPMCORE_NFS_S3_CLIENT_ID=s3-ak \
|
||||
-e CNPMCORE_NFS_S3_CLIENT_SECRET=s3-sk \
|
||||
-e CNPMCORE_NFS_S3_CLIENT_FORCE_PATH_STYLE=true \
|
||||
-e CNPMCORE_NFS_S3_CLIENT_DISABLE_URL=true \
|
||||
-e CNPMCORE_REDIS_HOST=127.0.0.1 \
|
||||
-e CNPMCORE_REDIS_PORT=6379 \
|
||||
-e CNPMCORE_REDIS_PASSWORD=your-redis-password \
|
||||
-e CNPMCORE_REDIS_DB=1 \
|
||||
--name cnpmcore-prod cnpmcore
|
||||
```
|
||||
|
||||
## 演示地址
|
||||
|
||||
https://registry-demo.fengmk2.com:9443
|
||||
|
||||
管理员账号:cnpmcore_admin/12345678
|
||||
|
||||
通过 npm login 可以登录
|
||||
|
||||
```bash
|
||||
npm login --registry=https://registry-demo.fengmk2.com:9443
|
||||
```
|
||||
|
||||
查看当前登录用户
|
||||
|
||||
```bash
|
||||
npm whoami --registry=https://registry-demo.fengmk2.com:9443
|
||||
```
|
||||
|
||||
## fengmk2/cnpmcore 镜像
|
||||
|
||||
https://hub.docker.com/r/fengmk2/cnpmcore
|
||||
|
||||
```bash
|
||||
docker pull fengmk2/cnpmcore
|
||||
```
|
||||
@@ -52,6 +52,7 @@
|
||||
"tsc:prod": "npm run clean && tsc -p ./tsconfig.prod.json",
|
||||
"prepublishOnly": "npm run tsc:prod",
|
||||
"start": "eggctl start --daemon && touch egg.status",
|
||||
"start:foreground": "eggctl start",
|
||||
"stop": "rm -f egg.status && sleep 15 && eggctl stop"
|
||||
},
|
||||
"repository": {
|
||||
@@ -102,6 +103,7 @@
|
||||
"npm-package-arg": "^10.1.0",
|
||||
"oss-cnpm": "^4.0.0",
|
||||
"p-map": "^4.0.0",
|
||||
"s3-cnpmcore": "^1.1.2",
|
||||
"semver": "^7.3.5",
|
||||
"ssri": "^8.0.1",
|
||||
"tar": "^6.1.13",
|
||||
|
||||
Reference in New Issue
Block a user