第一种,一是考虑到公司可能不会提供经费,二npm在国内访问很慢,就是花钱也买不到好的体验。
第二种,不能更新即 npm update, 不能使用semver(语义化版本规范)。
那么较好的选择就只剩下第三种和第四种。
下面将分别使用基于Sinopia和基于 cnpmjs.org 这两种方案来搭建私有npm仓库并进行总结。
安装
前置工作:配置nodejs及npm环境
npm install -g sinopia
启动
sinopia warn --- config file - /home/map/.config/sinopia/config.yaml warn --- http address - http://localhost:4873/
此时访问localhost:4873,可获取html文件并且服务端响应正常,表示安装成功。
$ curl localhost:4873 ...
服务端响应
$ sinopia ... http <-- 200, user: undefined, req: 'GET /', bytes: 0/10896
配置
运行sinopia,自动生成的工作目录如下(通过第一个warn可以看到具体路径):
$ tree /home/map/.config/sinopia/ /home/map/.config/sinopia/ |-- config.yaml //存放所有配置信息 |-- htpasswd //存放所有账户信息 `-- storage //存放私有npm包及缓存公有包 |-- npm_test | |-- npm_test-1.0.0.tgz | |-- npm_test-1.0.1.tgz | `-- package.json `-- sinopia `-- package.json 3 directories, 6 files
config.yaml默认配置
# This is the default config file. It allows all users to do anything, # so don't use it on production systems. # # Look here for more config file examples: # https://github.com/rlidwka/sinopia/tree/master/conf # path to a directory with all packages storage: ./storage //npm包存放的路径 auth: htpasswd: file: ./htpasswd //保存用户的账号密码等信息 # Maximum amount of users allowed to register, defaults to "+inf". # You can set this to -1 to disable registration. #max_users: 1000 //默认为1000,改为-1,禁止注册 # a list of other known repositories we can talk to uplinks: npmjs: url: https://registry.npmjs.org/ //拉取公共包的地址源,默认为npm的官网,可以使用淘宝的npm镜像地址 packages: //配置权限管理 '@*/*': # scoped packages access: $all publish: $authenticated '*': # allow all users (including non-authenticated users) to read and # publish all packages # # you can specify usernames/groupnames (depending on your auth plugin) # and three keywords: "$all", "$anonymous", "$authenticated" access: $all # allow all known users to publish packages # (anyone can register by default, remember?) publish: $authenticated # if package is not available locally, proxy requests to 'npmjs' registry proxy: npmjs # log settings logs: - {type: stdout, format: pretty, level: http} #- {type: file, path: sinopia.log, level: info}
外网访问配置
通过在config.yaml中修改服务默认的监听端口,从而可以通过外网访问 sinopia 仓库。
listen: 0.0.0.0:4873
外网通过http://[IP | 域名]:[端口]的形式来访问。
浏览器外网访问如图:
图4 外网访问示例
账号配置
config.yaml 中auth部分对应账号的管理,默认可以通过客户端npm adduser添加账号。可以通过max_users:-1禁止客户端创建,而通过我们修改htpasswd文件来管理用户。
htpasswd文件示例:
lisi:{SHA}????????????????=:autocreated 2016-02-05T15:39:19.960Z wangwu:{SHA}????????????????=:autocreated 2016-02-05T17:59:05.041Z
密码是被加密过的,是简单的SHA1哈稀之后再转换成 Base64 。
配置npm registry
建议客户端使用nrm 进行npm registry地址管理和切换
安装
npm install -g nrm
添加sinopia仓库地址
nrm add sinopia http://192.168.xx.xx:4873
切换私有仓库
nrm use sinopia
查看所有仓库地址(星标为当前仓库源)
nrm ls npm ---- https://registry.npmjs.org/ cnpm --- http://r.cnpmjs.org/ taobao - https://registry.npm.taobao.org/ nj ----- https://registry.nodejitsu.com/ rednpm - http://registry.mirror.cqupt.edu.cn/ npmMirror https://skimdb.npmjs.com/registry/ edunpm - http://registry.enpmjs.org/ * sinopia http://192.168.xx.xx:4873/
切换到私有仓库之后,发包的操作跟npm发包基本无差别。
登录账号之后:
npm publish + [email protected]
ps: 版本号重复的情况再次发布的包不会主动更新,并且发布不会有错误提示,更新包务必更新版本号。
发布成功后私有仓库站点会显示包情况,README.md文件会作为详情描述展开。
图5 包发布成功示例图
官方依赖如下图:
图6 官方依赖配置
我这边的配置:
安装 cnpmjs.org
npm i -g cnpmjs.org
我安装的是3.0.0-beta.1。
修改 cnpmjs.org 配置文件
cnpmjs.org 默认安装路径:/usr/local/lib/node_modules/cnpmjs.org
打开配置文件: cnpmjs.org/config/index.js
部分配置项说明
/* * server configure //服务器配置 */ registryPort: 7001, //仓库访问端口(执行发布安装) webPort: 7002, //展示查询站点访问端口 bindingHost: '', //监听绑定的 Host,默认127.0.0.1,外网访问注释掉此项即可 /** * database config //数据库相关设置 */ database: { db: 'cnpmjs', //数据库名称 username: 'root', //数据库访问账号 password: '123456', //数据库访问密码 // the sql dialect of the database // - currently supported: 'mysql', 'sqlite', 'postgres', 'mariadb' dialect: 'mysql', //使用数据库,默认sqlite,这里我们改成mysql // custom host; default: 127.0.0.1 host: '127.0.0.1', //数据库访问IP,通常127.0.0.1 // custom port; default: 3306 port: 3306, //数据库访问端口,通常3306 // 模块文件存储,默认将发布的私有模块跟缓存公共模块存储在本地文件系统中,路径~/.cnpmjs.org/nfs ,也就是模块文件都存储在这个目录下;或者可以选择三方储存方式比如七牛等,着这里配置插件;也支持接口开发扩展储存; nfs: require('fs-cnpm')({ dir: path.join(dataDir, 'nfs') }), // registry url name //模块注册列表访问域名,默认r.cnpmjs.org,安装模块时会到这个域名下查找,这个默认设置略坑,建议没有外网域名的先清空回头再配 registryHost: '', // default system admins //默认管理员账号 admins: { // name: email //fengmk2: '[email protected]', admin: '[email protected]', //dead_horse: '[email protected]', }, /* * registry mode config 私有模块发布相关配置 */ //是否开启私有模式,默认为 false; //私有模式下只有管理员能发布模块,其他账号只有同步权限 //非私有模式,注册用户都可以发布模块 enablePrivate: false, // registry scopes //若为非私有模式发布则此项必填,非管理员发布模块式命名必须以scopes字段开头,模块命名示例“@cnpm/packagename” //更多了解npm-scope请查阅https://docs.npmjs.com/misc/scope scopes: [ '@cnpm', '@cnpmtest', '@cnpm-test' ], // 私有模块非scopes白名单,各种非以scope方式发布的老模块的白名单管理,数组形式维护 privatePackages: [], /** * sync configs 同步源仓库相关设置 */ //npm官方registry地址,不会直接从这个地址同步模块,但有时会从这里获取模块信息,除非必要请勿更改 officialNpmRegistry: 'https://registry.npmjs.com', officialNpmReplicate: 'https://replicate.npmjs.com', //同步模块上游registry地址 sourceNpmRegistry: 'https://registry.npm.taobao.org', //上游registry是否是cnpm,默认true,若要使用npm官方地址作为同步上游,请设置为false sourceNpmRegistryIsCNpm: true, //若安装时模块不存在,是否向源registry进行同步,默认true syncByInstall: true, // 同步模式选项 // none: 不进行同步,只管理用户上传的私有模块,公共模块直接从上游获取 // exist: 只同步已经存在于数据库的模块 // all: 定时同步所有源registry的模块 syncModel: 'exist', // 'none', 'all', 'exist' // 同步时间间隔,默认10分钟 syncInterval: '10m', // 是否同步模块中devDependencies,默认false syncDevDependencies: false, //用户账号系统接入,可以扩展接入公司的账号系统 //本文暂不涉及,详见https://github.com/cnpm/cnpmjs.org/wiki/Use-Your-Own-User-Authorization userService: null, //另外一个比较坑的默认设置,默认false,踩坑记录里详细说 enableAbbreviatedMetadata: true,
新建数据库并导表
用户名、密码进入数据库
mysql -uroot -p123456
ps:-u用户名 -p密码,初次使用的mysql还没创建系统账号时默认为空, mysql命令后直接回车后即可进入命令行。创建mysql账号,注意名称跟cnpm配置中相对应。
创建cnpm所需的数据库
create database cnpmjs;
切换到cnpm数据库
use cnpmjs;
导入cnpm数据库配置文件
文件位于cpm安装目录docs/db.sql下
source docs/db.sql;
查看导入的表
我这个版本共导入16个表
show tables;
tip:数据库配置这块如果觉得命令行太麻烦,可以使用可视化数据库管理工具,例如phpAdmin等进行管理。
跑起来
cnpmjs.org start
访问对应的IP加访问端口即可看到,展示站点如下图
图7 start之后示意图
我们可以搜索我们想要的包,如果没有可以选择sync进行同步。
图8 选择同步
选择同步之后,后有如下同步过程
图9 同步过程
客户端我们需要安装cnpm工具进行模块管理
npm i -g cnpm
把cnpm的registry指向我们的私有npm服务ip,端口使用registry端口;
cnpm config set registry 192.xxx.x.x:7001
私有仓库用户登录,填写用户名密码邮箱
cnpm login
命名行发布
cnpm publish
cnpm的方案配置上要比sinopia复杂,所以踩的坑也多一点,在此记录一下。
(1) 数据库前期准备
在准备数据库的阶段mysql-V检查到系统已经预装了mysql,但运行mysql登录命令的时候却报错如下:
$ mysql ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)
于是我启动了服务,然鹅依旧报错
$ service mysqld start mysqld: unrecognized service
查解决方法改配置文件依然没有效果,最终发现mysql-server没有安装:disappointed_relieved:,这个报错完全看不出来是这个问题:joy:。。。
yum install mysql-server
安装之后重新运行server就好了。
(2) 展示站点数据库访问失败
创建数据库,导表,改配置之后运行起来,访问站点页面也没啥问题,but为啥没有数据??打开请求一看,获取数据的接口500了。
查了log发现报少表,打开数据库确认确实少表,果然删了重新导。过程中有三个表报‘Unknown character set: ‘utf8mb4’导入失败,第一次没注意。。。
查后发现mysql在mysql5.6+的版本中才支持utf8mb4的编码格式,而我们的版本只支持utf8。
那就只有两个选择,第一改编码格式,第二升级数据库。
改过三张表的编码后,顺利导入,站点获取数据正常。
(3) 模块安装一直失败
命令行报错
图10 命令行报错
其实这个请求地址一看就很可疑,这个地址正式config中registryHost的默认值,所以要么给这个域名绑IP访问,要么就把这设置项先空着,等有了外网域名的时候再改。
(4) 私有模块发布失败
报错反应模块命名不对,发布私有模块需要遵守scope的命名规范,带上相应的前缀名,具体情况参见npm-scope。
(5) 同步模块报错
sync error: TypeError: Cannot read property 'findAll' of null ... ...
这个问题最终是查issues解决的,貌似在1149加入了功能之后出现的,需要把enableAbbreviatedMetadata:设置为true解决issues#1236,#1289。
由于使用命令行起的服务需要通过使用进程管理来守护。
为了提升私有库服务稳定性,我们选择pm2守护进程。
安装pm2:
npm install -g pm2
使用pm2 启动 sinopia:
pm2 start sinopia
常用pm2操作
pm2 list //查看所有进程 pm2 logs //查看日志 pm2 start app_name|app_id //启动某个进程 pm2 stop app_name|app_id //停止某个进程 pm2 restart app_name|app_id //重启某个进程 pm2 delete app_name|app_id //删除某个进程
通过两种主流方案——基于Sinopia和基于cnpm搭建npm私有仓库——的尝试,我们发现:
俗话说:脱离业务场景谈解决方案,都是耍流氓。
如果你是一个想要拥有私人npm仓库的个人开发者或者小团队,Sinopia完全可以满足需求;如果想要对私人npm服务做更多个性化定制方案,那么cnpm的扩展性支持更加友好。