下载代码

1
$ git clone https://github.com/cnpm/cnpmjs.org.git

数据库(mysql)

  • 创建数据库
  • 导入数据表
    目录:docs/db.sql

修改配置文件

config/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 /**
* database config
*/

database: {
db: 'cnpmjs', //数据名称
username: 'root', //数据库登录用户名
password: '***', //数据登录密码

// the sql dialect of the database
// - currently supported: 'mysql', 'sqlite', 'postgres', 'mariadb'
dialect: 'mysql', //数据库类型

// custom host; default: 127.0.0.1
host: '127.0.0.1', //数据库主机

// custom port; default: 3306
port: 3306, //数据库端口号

// use pooling in order to reduce db connection overload and to increase speed
// currently only for mysql and postgresql (since v1.5.0)
pool: {
maxConnections: 10,
minConnections: 0,
maxIdleTime: 30000
},

dialectOptions: {
// if your server run on full cpu load, please set trace to false
trace: true,
},

// the storage engine for 'sqlite'
// default store into ~/.cnpmjs.org/data.sqlite
storage: path.join(dataDir, 'data.sqlite'),

logging: !!process.env.SQL_DEBUG,

// enable proxy npm audits request or not
enableNpmAuditsProxy: true,
}

启动

1
2
3
$ npm run start
#或者
$ node dispatch.js

发布包

  • 切换源
    1
    $ npm config set registry http://127.0.0.1:7001
  • 登录
    1
    2
    $ npm login
    #需输入配置文件中的用户名和对应的邮箱,第一次登录输入的密码即为设置的密码
  • 发布
    新建一个测试包目录,
    1
    2
    3
    4
    $ npm init
    #根据提示输入项目信息
    #增加index.js
    $ npm publish

搜索

发布完成后,打开http://127.0.0.1:7002
能看见当前搭建好的仓库主页,
搜索框中输入关键字进行搜索(搜索本地数据库中的信息),搜索后会出来结果列表
点击结果列表进入包详情,此时会跳转到https://registry.npm.taobao.org而不是本地的地址

问题1:本地包信息的查看

  • 原因:
    配置文件中的同步模式syncModel设置为none

  • 改进:
    在文件middleware/proxy_to_npm.js中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    return function* proxyToNpm(next) {
    // 此处判断了同步模式是否为`none`,如果不是none就没有问题。
    // 因为想本地只存储发布的库,而其他从上游cnpm或者npm下载的库都不同步
    // 但是`syncModel`设置为`exist`或者`all`时都会同步上游的库,`exist`只同步用到的,`all`是同步所有的
    // 因此需要在设置为`none`时,先判断本地是否存在该库,本地有就显示本地的。
    if (config.syncModel !== 'none') {
    return yield next;
    }

    // syncModel === none
    // only proxy read requests
    if (this.method !== 'GET' && this.method !== 'HEAD') {
    return yield next;
    }

    var pathname = decodeURIComponent(this.path);

    /**
    * 默认的同步模式none, 下载时会将所有的包链接跳转到`sourceNpmRegistry`
    * 更改为,先查本地数据库,如果是本地的就从本地下载,否则再从`sourceNpmRegistry`下载
    * 2019.10.17
    */
    var pathArray = pathname.split('/')
    var localPackage = yield packageService.getLatestModule(pathArray[pathArray.length-1])
    if(localPackage) {
    return yield next;
    }

    var isScoped = false;
    var isPublichScoped = false;
    // check scoped name
    if (config.scopes && config.scopes.length > 0) {
    for (var i = 0; i < scopedUrls.length; i++) {
    const m = scopedUrls[i].exec(pathname);
    if (m) {
    isScoped = true;
    if (config.scopes.indexOf(m[1]) !== -1) {
    // internal scoped
    isPublichScoped = false;
    } else {
    isPublichScoped = true;
    }
    break;
    }
    }
    }

    var isPublich = false;
    if (!isScoped) {
    for (var i = 0; i < proxyUrls.length; i++) {
    isPublich = proxyUrls[i].test(pathname);
    if (isPublich) {
    break;
    }
    }
    }

    if (isPublich || isPublichScoped) {
    var url = redirectUrl + this.url;
    debug('proxy isPublich: %s, isPublichScoped: %s, package to %s',
    isPublich, isPublichScoped, url);
    this.redirect(url);
    return;
    }

    yield next;
    };
  • 此处的改动会影响到包的下载,如果本地有就下载本地的,需要配置config/index.js:
    1
    2
    3
    // registry url name
    // 需要更改为实际使用到的域名或者ip和端口号
    registryHost: '127.0.0.1:7001'

配置仅管理员发布

1
2
3
4
5
// enable private mode or not
// private mode: only admins can publish, other users just can sync package from source npm
// public mode: all users can publish
// 设置为true
enablePrivate: true,

配置包域

1
2
3
// registry scopes, if don't set, means do not support scopes
// 如果没有配置,表示不支持
scopes: [ '@test' ],

查看本地发布的所有包

原仓库不支持,自行添加

  • services/package.js文件中添加方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
    * 显示所有的模块
    */
    exports.listAllModules = function* () {
    var rows = yield Module.findAll({
    attributes: [
    'name', 'description', 'version',
    ]
    });
    return rows;
    };
  • 添加controllers/web/package/list_all.js文件。内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    'use strict';

    var packageService = require('../../../services/package');

    module.exports = function* listAllModules() {
    var tasks = {};

    tasks = packageService.listAllModules()

    var packages = yield tasks;
    yield this.render('all', {
    title: 'all packages',
    packages: packages
    });
    };
  • routes/web.js文件中添加路由处理:

    1
    2
    3
    4
    // 首先引入方法
    var listAll = require('../controllers/web/package/list_all');
    // 再添加路由处理
    app.get('/all', listAll)
  • 在首页的总数中添加超链接,直接跳转到列表页面。更改public/js/readme.js文件:

    1
    2
    3
    $('#total-packages').html(${humanize(data.doc_count)});
    // 更改为
    $('#total-packages').html(`<a href="/all">${humanize(data.doc_count)}</a>`);
  • 添加view/web/all.html文件,内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    <style>
    #all .package {
    padding: 10px;
    font-size: 18px;
    border-bottom: 1px solid #ddd;
    }

    #all .alert a {
    font-size: 20px;
    }

    #all .package-description {
    margin: 0.5em 0;
    font-size: 16px;
    }

    </style>
    <div id="all">
    <h1>All packages</h1>
    <% if (!packages.length) { %>
    <div class="alert alert-warning">
    no package
    </div>
    <% } else {%>
    <% for (var i = 0; i < packages.length; i++) {
    var item = packages[i];
    %>
    <div class="package">
    <a href="/package/<%= item.name %>" class="package-name"><%= item.name %></a>
    <p class="package-description"><%= item.description %></p>
    </div>
    <% } %>
    <% } %>
    </div>

添加自定义readme文件

配置文件config/index.js中更改:

1
2
// 自定义首页显示的readme文件
customReadmeFile: 'docs/web/custome_readme.md', // you can use your custom readme file instead the cnpm one

本文地址: http://gehaiqing.com/2019/10/21/cnpmjs-build-private-registry/