一、安装系统并配置编译环境
1.下载 CentOS 7 系统镜像并安装,选择基本开发环境。
2.修改默认 yum 源为阿里云源,并添加 EPEL 源。
1 2 3 4 5 6 7 |
# 如提示 wget 不存在,可使用 curl 下载 # 备份默认源 sudo cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak # 修改为阿里源 sudo wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo # 添加 EPEL 源 sudo wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo |
3.安装 gcc 编译环境
1 |
sudo yum install gcc-c++ make |
4.验证安装结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# 验证 g++ 版本 g++ -v 使用内建 specs。 COLLECT_GCC=g++ COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper 目标:x86_64-redhat-linux 配置为:../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux 线程模型:posix gcc 版本 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC) # 验证 make 版本 make -v GNU Make 3.82 Built for x86_64-redhat-linux-gnu Copyright (C) 2010 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. |
二、监测环境并安装 node.js
1.执行环境检测脚本,下载 node 在相应环境的已编译包
1 |
curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash - |
2.安装 node 和 npm
1 |
sudo yum -y install nodejs |
3.验证安装结果
1 2 3 4 5 6 7 |
# 验证 node 版本 node -v v8.9.4 # 验证 npm 版本 npm -v 5.6.0 |
三、安装并配置 cnpmjs.org
1.安装 cnpmjs.org依赖
1 2 3 4 5 6 7 8 9 10 |
# 安装 cnpm npm install -g cnpm --registry=https://registry.npm.taobao.org # 安装 node-pre-gyp cnpm install -g node-pre-gyp # 安装 sqlite3 直接安装会报错 node-pre-gyp: 未找到命令 cnpm install -g sqlite3 # 安装 cnpmjs.org cnpm install -g cnpmjs.org # 创建配置目录 ~/.cnpmjs.org cnpmjs.org start |
2.验证安装结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# 验证 cnpm 版本 cnpm -v cnpm@5.2.0 (/usr/lib/node_modules/cnpm/lib/parse_argv.js) npm@5.7.1 (/usr/lib/node_modules/cnpm/node_modules/npm/lib/npm.js) node@8.9.4 (/usr/bin/node) npminstall@3.3.0 (/usr/lib/node_modules/cnpm/node_modules/npminstall/lib/index.js) prefix=/usr linux x64 3.10.0-327.el7.x86_64 registry=https://registry.npm.taobao.org # 验证 node-pre-gyp 版本 node-pre-gyp -v v0.6.39 # 验证 sqlite3 版本 sqlite3 -version 3.7.17 2013-05-20 00:56:22 118a3b35693b134d56ebd780123b7fd6f1497668 # 验证 cnpmjs.org 版本 cnpmjs.org --version 2.19.4 |
3.添加防火墙例外
1 2 3 4 5 6 7 8 9 10 |
# 检查防火墙状态 systemctl status firewalld # 启动防火墙,未启动会报错 FirewallD is not running systemctl start firewalld # 添加 7001 端口 firewall-cmd --zone=public --add-port=7001/tcp --permanent # 添加 7002 端口 firewall-cmd --zone=public --add-port=7002/tcp --permanent # 根据需要,关闭防火墙 systemctl stop firewalld |
四、修改 cnpmjs.org 配置
1.修改 /root/.cnpmjs.org/config.json (实际使用中必须删除注释,否则 json 格式错误)
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 |
{ "registryPort": 7001, // cnpmjs.org web 站点端口 "webPort": 7002, // cnpmjs.org register 端口 "bindingHost": "0.0.0.0", // 允许外部访问 "handleSyncRegistry": "http://127.0.0.1:7001", // 同步源 "registryHost": "registry.npm.xxxxxx.work", // cnpmjs.org register 域名 "scopes": [ "@xxxxxx" // 私有包 scope 名称 ], "enablePrivate": false, // 允许所有登录用户发布私有包 "syncModel": "none", // 不同步公共包 "alwaysAuth": false, // 不强制用户认证 "customReadmeFile": "/root/.cnpmjs.org/docs/web/readme.md", // 自定义首页 "userService": "/root/.cnpmjs.org/services/custom_user_service.js", // 自定义用户认证 "admins": { "admin": "admin@xxxx.com" // cnpmjs.org 管理员 }, "database": { // cnpmjs.org 数据库 "db": "cnpmjs_test", // 数据库名称 "username": "root", // 数据库用户名 "password": "", // 数据库密码 "dialect": "sqlite", // 数据库类型 "host": "127.0.0.1", // 数据库 IP "port": 3306, // 数据库端口 "storage": "/root/.cnpmjs.org/data.sqlite" // sqlite 数据库位置 } } |
2.修改 cnpmjs.org 源码 /usr/lib/node_modules/cnpmjs.org/services/user.js
1 2 3 4 |
11 + if(typeof(config.userService) === 'string') { 12 + var CustomUserService = require(config.userService); 13 + config.userService = new CustomUserService(); 14 + } |
3.自定义首页 /root/.cnpmjs.org/docs/web/readme.md
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# 公司 npm 私有仓储 [npm.xxxxxx.work](http://npm.xxxxxx.work) 基于 [cnpmjs.org](https://github.com/cnpm/cnpmjs.org) 搭建,使用 @xxxxxx 作为私有仓储的 [scope](https://docs.npmjs.com/getting-start) 仅用于托管公司私有包,不同步公共包,请使用 npm 客户端。 ## npm 客户端配置 1. 拦截 @xxxxxx 作用域下所有操作至私有仓储,可安装公司私有包。 ```bash $ npm config set @xxxxxx:registry http://registry.npm.xxxxxx.work ``` 2. 使用 [git.xxxxxx.work](http://git.xxxxxx.work) 用户名和[个人访问令牌](http://git.xxxxxx.work/profile/personal_access_tokens)登录,可发布公司私有包。 ```bash $ npm login --scope=@xxxxxx --registry=http://registry.npm.xxxxxx.work Username: zhangsan # 用户名 Password: xxxxxxxxxxxxxxxxxxxx # 个人访问令牌(需勾选 Scopes 中 api 选项) Email: (this IS public) zhangsan@xxxx.com # 邮箱 ``` ## 更多信息请查看[使用说明](http://xxxxxx.wiki/pages/viewpage.action?pageId=8524381) **此处粘贴 /usr/lib/node_modules/cnpmjs.org/docs/web/readme.md 文件内容** |
4.自定义用户认证 /root/.cnpmjs.org/services/custom_user_service.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 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
const http = require('http'); const isAdmin = require('cnpmjs.org/lib/common').isAdmin; const config = require('cnpmjs.org/config'); // User: https://github.com/cnpm/cnpmjs.org/wiki/Use-Your-Own-User-Authorization#user-data-structure // { // "login": "fengmk2", // "email": "fengmk2@gmail.com", // "name": "Yuan Feng", // "html_url": "http://fengmk2.github.com", // "avatar_url": "https://avatars3.githubusercontent.com/u/156269?s=460", // "im_url": "", // "site_admin": false, // "scopes": ["@org1", "@org2"] // } const emailDomain = '@xxxx.com'; const defaultToken = 'xxxxxxxxxxxxxxxxxxxx'; function convertToUser(gitUser) { let user = { login: gitUser.username, email: gitUser.email || `${gitUser.username}${emailDomain}`, name: gitUser.name, html_url: gitUser.web_url, avatar_url: gitUser.avatar_url, im_url: '', site_admin: isAdmin(gitUser.username), scopes: config.scopes, }; return user; } function gitHttp(api, token) { return new Promise((resolve, reject) => { let options = { method: 'GET', port: 80, hostname: 'git.xxxxxx.work', path: `/api/v4${api}`, headers: { 'PRIVATE-TOKEN': token } }; let req = http.request(options, (res) => { if (res.statusCode === 200) { res.setEncoding('utf8'); let rawData = ''; res.on('data', (chunk) => { rawData += chunk; }); res.on('end', () => { try { let parsedData = JSON.parse(rawData); console.log(`[CustomUserService] [${api}] [${token}] JSON.parse: success`, parsedData); resolve(parsedData); } catch (e) { reject(`[CustomUserService] [${api}] [${token}] JSON.parse: ${e.message}`, e); } }); } else { reject(`[CustomUserService] [${api}] [${token}] statusCode: ${res.statusCode}`); } }); req.on('error', (e) => { reject(`[CustomUserService] [${api}] [${token}] error: ${e.message}`, e); }); req.end(); }); } function* gitAuth(username, token) { let data = yield gitHttp('/user', token); let gitUser = null; if (data && data.username === username) { gitUser = data; } return gitUser; } function* gitGet(username) { let data = yield gitHttp(`/users?username=${encodeURIComponent(username)}`, defaultToken); let gitUser = null; if (data && data.length > 0 && data[0].username === username) { gitUser = data[0]; } return gitUser; } function* gitList(usernames) { let gitUsers = []; if (usernames && usernames.length > 0) { for (let i = 0; i < usernames.length; i++) { let username = usernames[i]; let gitUser = yield gitGet(username); if (gitUser) { gitUsers.push(gitUser); } } } return gitUsers; } function* gitSearch(query, limit) { let data = yield gitHttp(`/users?search=${encodeURIComponent(query)}&per_page=${limit}`, defaultToken); let gitUsers = []; if (data && data.length > 0) { gitUsers = data; } return gitUsers; } function CustomUserService() { } const proto = CustomUserService.prototype; /** * Auth user with login name and password * @param {String} login login name * @param {String} password login password * @return {User} */ proto.auth = function* (login, password) { console.log(`[CustomUserService] [auth]`, login, password); let user = null; try { let gitUser = yield gitAuth(login, password); if (gitUser) { user = convertToUser(gitUser); } } catch (e) { console.log(`[CustomUserService] [auth]`, e); } return user; }; /** * Get user by login name * @param {String} login login name * @return {User} */ proto.get = function* (login) { console.log(`[CustomUserService] [get]`, login); let user = null; try { let gitUser = yield gitGet(login); if (gitUser) { user = convertToUser(gitUser); } } catch (e) { console.log(`[CustomUserService] [get]`, e); } return user; }; /** * List users * @param {Array<String>} logins login names * @return {Array<User>} */ proto.list = function* (logins) { console.log(`[CustomUserService] [list]`, logins); let users = []; try { let gitUsers = yield gitList(logins); gitUsers.forEach((gitUser) => { users.push(convertToUser(gitUser)); }); } catch (e) { console.log(`[CustomUserService] [list]`, e); } return users; }; /** * Search users * @param {String} query query keyword * @param {Object} [options] optional query params * - {Number} limit match users count, default is `20` * @return {Array<User>} */ proto.search = function* (query, options) { console.log(`[CustomUserService] [search]`, query, options); let users = []; try { options = options || {}; options.limit = parseInt(options.limit); if (!options.limit || options.limit < 0) { options.limit = 20; } let gitUsers = yield gitSearch(query, options.limit); gitUsers.forEach((gitUser) => { users.push(convertToUser(gitUser)); }); } catch (e) { console.log(`[CustomUserService] [search]`, e); } return users; }; module.exports = CustomUserService; |
5.验证自定义配置
1 2 3 4 5 6 7 8 9 |
export NODE_PATH=/usr/lib/node_modules && /usr/bin/cnpmjs.org start Starting cnpmjs.org ... cluster: false admins: {"admin":"admin@xxxx.com"} scopes: ["@xxxxxx"] sourceNpmRegistry: https://registry.npm.taobao.org syncModel: none [Tue Mar 06 2018 17:05:20 GMT+0800 (CST)] [worker:8004] Server started, registry server listen at 0.0.0.0:7001, web listen at 0.0.0.0:7002, cluster: false |
五、开机启动 cnpmjs.org 并配置域名和定期备份
1.创建服务并开机自启 /usr/lib/systemd/system/cnpmjs.org.service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[Unit] Description=cnpmjs.org After=network.target [Service] Type=simple User=root Environment=NODE_PATH=/usr/lib/node_modules ExecStart=/usr/bin/cnpmjs.org start ExecStop=/usr/bin/cnpmjs.org stop PrivateTmp=true [Install] WantedBy=multi-user.target |
2.应用并启动 cnpmjs.org 服务
1 2 3 4 5 6 7 8 |
# 刷新服务守护进程 systemctl daemon-reload # 启动 cnpmjs.org 服务 systemctl start cnpmjs.org.service # 设置开机启动 systemctl enable cnpmjs.org.service # 查看启动日志 journalctl -u cnpmjs.org.service |
3.配置 nginx 反向代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# cnpmjs.org registry server { listen 80; server_name registry.npm.xxxxxx.work; location / { #proxy_redirect off; proxy_pass http://192.168.21.23:7001; } } # cnpmjs.org web server { listen 80; server_name npm.xxxxxx.work; location / { #proxy_redirect off; proxy_pass http://192.168.21.23:7002; } } |
4.验证域名配置
1 2 |
curl npm.xxxxxx.work curl registry.npm.xxxxxx.work |
5.创建备份脚本 /root/.cnpmjs.org/backup/backup.sh
由于 cnpmjs.org 使用文件方式存放已发布的包,因此只用备份 sqlite 数据库即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#!/bin/sh # 当前时间 var_dateTime=$(date +%Y%m%d%H%M) # cnpmjs.org 配置目录 var_workDir="/root/.cnpmjs.org" # 备份目录 var_backupDir=${var_workDir}"/backup/"${var_dateTime} # 创建备份目录 mkdir -p ${var_backupDir} # 数据库源位置 var_dataSource=${var_workDir}"/data.sqlite" # 数据库目标位置 var_dataTarget=${var_backupDir}"/data.sqlite" # 备份数据库 sqlite3 ${var_dataSource} ".backup ${var_dataTarget}" |
6.验证备份脚本
1 2 3 4 5 6 7 8 |
# 修改备份脚本执行权限 chmod a+x /root/.cnpmjs.org/backup/backup.sh # 执行备份脚本 /root/.cnpmjs.org/backup/backup.sh # 检查是否已经生成备份 ls -l /root/.cnpmjs.org/backup/ |
7.配置定时任务
1 2 3 4 5 6 7 8 9 |
# 编辑定时任务文件 /var/spool/cron/root crontab -e # 每周日 00:00 执行备份 0 0 * * 0 /root/.cnpmjs.org/backup/backup.sh # 如果未正常进行,检查以下文件 cat /var/log/cron cat /var/spool/mail/root |
六、【非服务端】npm 或 cnpm 客户端配置
1.拦截 @xxxxxx 作用域下所有操作至私有仓储,可安装公司私有包。
1 2 |
# 或替换为 cnpm 命令 npm config set @xxxxxx:registry http://registry.npm.xxxxxx.work |
2.使用 git.xxxxxx.work 用户名和个人访问令牌登录,可发布公司私有包。
1 2 3 4 5 6 |
# 或替换为 cnpm 命令 npm login --scope=@xxxxxx --registry=http://registry.npm.xxxxxx.work Username: zhangsan # 用户名 Password: xxxxxxxxxxxxxxxxxxxx # 个人访问令牌(需勾选 Scopes 中 api 选项) Email: (this IS public) zhangsan@xxxx.com # 邮箱 |
参考资料:
CentOS 7 使用阿里云的yum源
Installing Node.js via package manager
CentOS7使用firewalld打开关闭防火墙与端口
Nginx+Center OS 7.2 开机启动设置(转载)
Using the SQLite Online Backup API
CentOS下使用crontab命令来定时执行任务
crontab中命令行中的百分号(%)需要做转义
Deploy a private npm registry in 5 minutes]
Different Modes
cnpmjs.org/config/index.js
GitLab API
Running Your Node.js App With Systemd – Part 1
Running Your Node.js App With Systemd – Part 2
npm 混合公共仓库和私有仓库
《在 CentOS 7 平台使用 cnpmjs.org 搭建 npm 私有仓储,并集成 gitlab 认证》上有1条评论