diff --git a/.gitignore b/.gitignore
index c4fa1f63..c8f8d743 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,4 +14,8 @@ pids
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
node_modules
-coverage
\ No newline at end of file
+coverage
+
+# vs code
+.history
+.idea
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 0d59c5dd..3a0d6580 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,6 +13,7 @@ services:
- mysql
node_js:
+ - "8.10.0"
- "7.5"
- "7"
- "6.9.5"
@@ -27,4 +28,4 @@ before_install:
- "test ! -d node_modules || npm prune"
- "test ! -d node_modules || npm rebuild"
script: "npm run-script coverage"
-after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls"
\ No newline at end of file
+after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls"
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..c6903211
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,67 @@
+# Changelog for code-push-server
+
+## 0.5.x
+
+## 新特性
+- 针对文本增量更新进行优化,使用google `diff-match-patch` 算法计算差异
+ - react-native-code-push Android客户端适配,需要合并https://github.com/Microsoft/react-native-code-push/pull/1393, 才能正常使用文本增量更新功能。
+ - react-native-code-push iOS客户端适配 (需要合并https://github.com/Microsoft/react-native-code-push/pull/1399)
+ - react-native-code-push Windows客户端适配 (进行中)
+
+## fixbug
+
+- 修复统计数据激活数
+- 修复灰度发布bug
+- rollback后增加计算和最后一次增量更新版本
+
+## 如何升级到该版本
+
+### 升级数据库
+
+`$ npm run upgrade`
+
+or
+
+`$ code-push-server-db upgrade`
+
+
+## 0.4.x
+
+### 新特性
+
+- targetBinaryVersion 支持正则匹配, `deployments_versions`新增字段`min_version`,`max_version`
+ - `*` 匹配所有版本
+ - `1.2.3` 匹配特定版本`1.2.3`
+ - `1.2`/`1.2.*` 匹配所有1.2补丁版本
+ - `>=1.2.3<1.3.7`
+ - `~1.2.3` 匹配`>=1.2.3<1.3.0`
+ - `^1.2.3` 匹配`>=1.2.3<2.0.0`
+- 添加docker编排服务部署,更新文档
+- Support Tencent cloud cos storageType
+
+## 如何升级到该版本
+
+- 升级数据库
+`$ ./bin/db upgrade`
+or
+`$ mysql codepush < ./sql/codepush-v0.4.0-patch.sql`
+
+- 处理存量数据
+``` shell
+ $ git clone https://github.com/lisong/tools
+ $ cd tools
+ $ npm i
+ $ vim ./bin/fixMinMaxVersion //修改数据配置
+ $ node ./bin/fixMinMaxVersion //出现提示 success
+```
+
+## 0.3.x
+
+- 支持灰度发布
+- 适配`code-push app add` 命令,应用不再以名字区分平台,而是以类型区分平台
+ - 数据库表apps新增字段`os`,`platform`
+- 完善`code-push release/release-react/release-cordova` 命令
+ - 数据库表packages新增`is_disabled`,`rollout`字段
+- 适配`code-push patch`命令
+- 新增`log_report_download`,`log_report_deploy`日志表
+- 升级npm依赖包
diff --git a/Makefile b/Makefile
index 0ef8c45e..7e59757b 100644
--- a/Makefile
+++ b/Makefile
@@ -8,18 +8,20 @@ test: test-integration
test-integration:
@echo "\nRunning integration tests..."
+ @NODE_ENV=test CONFIG_FILE=${ROOT}/config/config.test.js mocha test/api/init
@NODE_ENV=test PORT=3000 HOST=127.0.0.1 CONFIG_FILE=${ROOT}/config/config.test.js node bin/www &
@NODE_ENV=test CONFIG_FILE=${ROOT}/config/config.test.js mocha \
- test/api/init test/api/users test/api/auth test/api/account test/api/accessKeys test/api/sessions test/api/apps test/api/index --recursive --timeout 15000
+ test/api/users test/api/auth test/api/account test/api/accessKeys test/api/apps test/api/index --recursive --timeout 15000
coverage:
@echo "\n\nRunning coverage report..."
rm -rf coverage
# @NODE_ENV=test CONFIG_FILE=${ROOT}/config/config.test.js ./node_modules/istanbul/lib/cli.js cover --report lcovonly --dir coverage/core ./node_modules/.bin/_mocha \
# test/unit -- -R spec --recursive --timeout 15000
+ @NODE_ENV=test CONFIG_FILE=${ROOT}/config/config.test.js mocha test/api/init
@NODE_ENV=test PORT=3000 HOST=127.0.0.1 CONFIG_FILE=${ROOT}/config/config.test.js node bin/www &
@NODE_ENV=test CONFIG_FILE=${ROOT}/config/config.test.js ./node_modules/istanbul/lib/cli.js cover --report lcovonly --dir coverage/api ./node_modules/.bin/_mocha \
- test/api/init test/api/users test/api/auth test/api/account test/api/accessKeys test/api/sessions test/api/apps test/api/index -- -R spec --recursive --timeout 15000
+ test/api/users test/api/auth test/api/account test/api/accessKeys test/api/apps test/api/index -- -R spec --recursive --timeout 15000
@NODE_ENV=test CONFIG_FILE=${ROOT}/config/config.test.js ./node_modules/istanbul/lib/cli.js report
.PHONY: coverage
\ No newline at end of file
diff --git a/README.md b/README.md
index 86972353..1611e8db 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# CodePush Server [source](https://github.com/lisong/code-push-server)
+[](https://nodei.co/npm/code-push-server/)
+
[](https://npmjs.org/package/code-push-server)
[](https://nodejs.org/en/download/)
[](https://travis-ci.org/lisong/code-push-server)
@@ -9,252 +11,87 @@
[](https://snyk.io/test/npm/code-push-server)
[](https://spdx.org/licenses/MIT)
-CodePush Server is a CodePush progam server! microsoft CodePush cloud is slow in China, we can use this to build our's. I use [qiniu](http://www.qiniu.com/) to store the files, because it's simple and quick! Or you can use local storage, just modify config.js file, it's simple configure.
+CodePush Server is a CodePush progam server! microsoft CodePush cloud is slow in China, we can use this to build our's. I use [qiniu](http://www.qiniu.com/) to store the files, because it's simple and quick! Or you can use [local/s3/oss/tencentcloud] storage, just modify config.js file, it's simple configure.
+
-## qq交流群 535491067
+## Support Storage mode
+
+- local *storage bundle file in local machine*
+- qiniu *storage bundle file in [qiniu](http://www.qiniu.com/)*
+- s3 *storage bundle file in [aws](https://aws.amazon.com/)*
+- oss *storage bundle file in [aliyun](https://www.aliyun.com/product/oss)*
+- tencentcloud *storage bundle file in [tencentcloud](https://cloud.tencent.com/product/cos)*
## 正确使用code-push热更新
-- 苹果允许使用热更新[Apple's developer agreement](https://developer.apple.com/programs/ios/information/iOS_Program_Information_4_3_15.pdf), 但是规定不能弹框提示用户更新,影响用户体验。 而Google Play恰好相反,必须弹框告知用户更新。然而中国的android市场都必须关闭更新弹框,否则会在审核应用时以“请上传最新版本的二进制应用包”驳回应用。
+- 苹果App允许使用热更新[Apple's developer agreement](https://developer.apple.com/programs/ios/information/iOS_Program_Information_4_3_15.pdf), 为了不影响用户体验,规定必须使用静默更新。 Google Play不能使用静默更新,必须弹框告知用户App有更新。中国的android市场必须采用静默更新(如果弹框提示,App会被“请上传最新版本的二进制应用包”原因驳回)。
- react-native 不同平台bundle包不一样,在使用code-push-server的时候必须创建不同的应用来区分(eg. CodePushDemo-ios 和 CodePushDemo-android)
- react-native-code-push只更新资源文件,不会更新java和Objective C,所以npm升级依赖包版本的时候,如果依赖包使用的本地化实现, 这时候必须更改应用版本号(ios修改Info.plist中的CFBundleShortVersionString, android修改build.gradle中的versionName), 然后重新编译app发布到应用商店。
- 推荐使用code-push release-react 命令发布应用,该命令合并了打包和发布命令(eg. code-push release-react CodePushDemo-ios ios -d Production)
- 每次向App Store提交新的版本时,也应该基于该提交版本同时向code-push-server发布一个初始版本。(因为后面每次向code-push-server发布版本时,code-puse-server都会和初始版本比较,生成补丁版本)
-## EXAMPLE
-api.code-push.com 只是一个测试server,不要将自己生产环境的项目放在上面,服务器的宽带只有1M,而且服务没有做负载均衡和监控,稳定性不能保证,烦请大家自己搭建自己的服务。
-### shell命令行端
+### shell login
```shell
-$ code-push login http://api.code-push.com:8080 #登录
+$ code-push login http://api.code-push.com #登录
```
-### [web](http://www.code-push.com:8080)
+### [web](http://www.code-push.com)
-访问:http://www.code-push.com:8080
+访问:http://www.code-push.com
-### 客户端eg.
+### client eg.
[ReactNative CodePushDemo](https://github.com/lisong/code-push-demo-app)
[Cordova CodePushDemo](https://github.com/lisong/code-push-cordova-demo-app)
-## INSTALL FROM NPM PACKAGE
-
-```shell
-$ npm install code-push-server -g
-$ code-push-server-db init --dbhost localhost --dbuser root --dbpassword #初始化mysql数据库
-$ code-push-server #启动服务 浏览器中打开 http://127.0.0.1:3000
-```
+## HOW TO INSTALL code-push-server
-## INSTALL FROM SOURCE CODE
+- [docker](https://github.com/lisong/code-push-server/blob/master/docker/README.md) (recommend)
+- [manual operation](https://github.com/lisong/code-push-server/blob/master/docs/README.md)
-```shell
-$ git clone https://github.com/lisong/code-push-server.git
-$ cd code-push-server
-$ npm install
-$ ./bin/db init --dbhost localhost --dbuser root --dbpassword #初始化mysql数据库
-$ ./bin/www #启动服务 浏览器中打开 http://127.0.0.1:3000
-```
+## DEFAULT ACCOUNT AND PASSWORD
-## UPGRADE
+- account: `admin`
+- password: `123456`
-*from source code*
+## HOW TO USE
-```shell
-$ cd /path/to/code-push-server
-$ git pull --rebase origin master
-$ ./bin/db upgrade --dbhost localhost --dbuser root --dbpassword #升级codepush数据库
-$ #restart code-push-server
-```
+- [normal](https://github.com/lisong/code-push-server/blob/master/docs/react-native-code-push.md)
+- [react-native-code-push](https://github.com/Microsoft/react-native-code-push)
+- [code-push](https://github.com/Microsoft/code-push)
-*from npm package*
-```shell
-$ code-push-server-db upgrade --dbhost localhost --dbuser root --dbpassword #升级codepush数据库
-$ #restart code-push-server
-```
+## ISSUES
-## CONFIG
-```shell
-$ vim config/config.js
-```
-请检查如下配置是否和你的环境一致,尤其是downloadUrl参数
+[code-push-server normal solution](https://github.com/lisong/code-push-server/issues/135)
-```
- db: {
- username: "root",
- password: null,
- database: "codepush",
- host: "127.0.0.1",
- dialect: "mysql"
- },
- //七牛云存储配置 当storageType为qiniu时需要配置
- qiniu: {
- accessKey: "",
- secretKey: "",
- bucketName: "",
- downloadUrl: "" //文件下载域名地址
- },
- //阿里云存储配置 当storageType为oss时需要配置
- oss: {
- accessKeyId: "",
- secretAccessKey: "",
- endpoint: "",
- bucketName: "",
- prefix: "", // 对象Key的前缀,允许放到子文件夹里面
- downloadUrl: "", // 文件下载域名地址,需要包含前缀
- },
- //文件存储在本地配置 当storageType为local时需要配置
- local: {
- storageDir: "/Users/tablee/workspaces/storage",
- //文件下载地址 CodePush Server 地址 + '/download' download对应app.js里面的地址
- downloadUrl: "http://localhost:3000/download",
- // public static download spacename.
- public: '/download'
- },
- jwt: {
- // 登录jwt签名密钥,必须更改,否则有安全隐患,可以使用随机生成的字符串
- // Recommended: 63 random alpha-numeric characters
- // Generate using: https://www.grc.com/passwords.htm
- tokenSecret: 'INSERT_RANDOM_TOKEN_KEY'
- },
- common: {
- dataDir: "/Users/tablee/workspaces/data",
- //选择存储类型,目前支持local,oss,qiniu,s3配置
- storageType: "local"
- },
-```
-read [config.js](https://github.com/lisong/code-push-server/blob/master/config/config.js)
+[An unknown error occurred](https://github.com/lisong/code-push-server/issues?utf8=%E2%9C%93&q=unknown)
+[modify password](https://github.com/lisong/code-push-server/issues/43)
-## Storage mode [local/qiniu/s3]
-- 配置local存储,修改config/config.js中storageType值为local,配置中local下面storageDir和downloadUrl,如果不在同一台机器上,downloadUrl请指定域名或者ip地址
+# UPDATE TIME LINE
+- targetBinaryVersion support
+ - `*`
+ - `1.2.3`
+ - `1.2`/`1.2.*`
+ - `1.2.3 - 1.2.7`
+ - `>=1.2.3 <1.2.7`
+ - `~1.2.3`
+ - `^1.2.3`
-## RUN
-```shell
-$ node ./bin/www # or code-push-server
-```
-
-or point config file and ENV
-
-```shell
-$ CONFIG_FILE=/path/to/config.js NODE_ENV=production node ./bin/www # or CONFIG_FILE=/path/to/config.js NODE_ENV=production code-push-server
-```
-
-notice. you have to change `tokenSecret` in config.js for security.
-
-## Default listen Host/Port 0.0.0.0/3000
-you can change like this.
-
-```shell
-$ PORT=3000 HOST=127.0.0.1 NODE_ENV=production node ./bin/www # or PORT=3000 HOST=127.0.0.1 NODE_ENV=production code-push-server
-```
-
-## [code-push-cli](https://github.com/Microsoft/code-push)
-Use code-push-cli manager CodePushServer
-
-```shell
-$ npm install code-push-cli@latest -g
-$ code-push login http://127.0.0.1:3000 #login in browser account:admin password:123456
-```
+## Advance Feature
-change admin password eg.
+> use google diff-match-patch calculate text file diff patch
-```shell
-$ curl -X PATCH -H "Authorization: Bearer mytoken" -H "Accept: application/json" -H "Content-Type:application/json" -d '{"oldPassword":"123456","newPassword":"654321"}' http://127.0.0.1:3000/users/password
-```
-
-## [react-native-code-push](https://github.com/Microsoft/react-native-code-push) for react-native
-
-```shell
-$ cd /path/to/project
-$ npm install react-native-code-push@latest --save
-```
-
-## config react-native project
-Follow the react-native-code-push docs, addition iOS add a new entry named CodePushServerURL, whose value is the key of ourself CodePushServer URL. Andriod use the new CodePush constructor in MainApplication point CodePushServerUrl
-
-iOS eg. in file Info.plist
-
-```xml
-...
-CodePushDeploymentKey
-YourCodePushKey
-CodePushServerURL
-YourCodePushServerUrl
-...
-```
-
-Android eg. in file MainApplication.java
-
-```java
-@Override
-protected List getPackages() {
- return Arrays.asList(
- new MainReactPackage(),
- new CodePush(
- "YourKey",
- MainApplication.this,
- BuildConfig.DEBUG,
- "YourCodePushServerUrl"
- )
- );
-}
-```
-
-
-## [cordova-plugin-code-push](https://github.com/Microsoft/cordova-plugin-code-push) for cordova
-
-```shell
-$ cd /path/to/project
-$ cordova plugin add cordova-plugin-code-push@latest --save
-```
-
-## config cordova project
-
-edit config.xml. add code below.
-
-```xml
-
-
-
-
-
-
-
-
-```
-
-## Production Manage
-use [pm2](http://pm2.keymetrics.io/) to manage process.
-
-```shell
-$ npm install pm2 -g
-$ cp config/config.js /path/to/production/config.js
-$ vim /path/to/production/config.js #configure your env.
-$ cp docs/process.json /path/to/production/process.json
-$ vim /path/to/production/process.json #configure your env.
-$ pm2 start /path/to/production/process.json
-```
-
-## Use [CodePush Web](https://github.com/lisong/code-push-web) manage apps
-
-add codePushWebUrl config in ./config/config.js
-
-eg.
-
-```json
-...
-"common": {
- "codePushWebUrl": "Your CodePush Web address",
-}
-...
-```
+- support iOS and Android
+- use `"react-native-code-push": "git+https://git@github.com/lisong/react-native-code-push.git"` instead `"react-native-code-push": "x.x.x"` in `package.json`
+- change `apps`.`is_use_diff_text` to `1` in mysql codepush database
## License
MIT License [read](https://github.com/lisong/code-push-server/blob/master/LICENSE)
diff --git a/app.js b/app.js
index 172965ab..ee93e3cc 100644
--- a/app.js
+++ b/app.js
@@ -9,9 +9,9 @@ var _ = require('lodash');
var fs = require('fs');
var routes = require('./routes/index');
+var indexV1 = require('./routes/indexV1');
var auth = require('./routes/auth');
var accessKeys = require('./routes/accessKeys');
-var sessions = require('./routes/sessions');
var account = require('./routes/account');
var users = require('./routes/users');
var apps = require('./routes/apps');
@@ -33,16 +33,16 @@ app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
//use nginx in production
-if (app.get('env') === 'development') {
+//if (app.get('env') === 'development') {
log.debug("set Access-Control Header");
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
- res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
+ res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, X-CodePush-Plugin-Version, X-CodePush-Plugin-Name, X-CodePush-SDK-Version");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,PATCH,DELETE,OPTIONS");
log.debug("use set Access-Control Header");
next();
});
-}
+//}
log.debug("config common.storageType value: " + _.get(config, 'common.storageType'));
@@ -73,9 +73,9 @@ if (_.get(config, 'common.storageType') === 'local') {
}
app.use('/', routes);
+app.use('/v0.1/public/codepush', indexV1);
app.use('/auth', auth);
app.use('/accessKeys', accessKeys);
-app.use('/sessions', sessions);
app.use('/account', account);
app.use('/users', users);
app.use('/apps', apps);
@@ -93,34 +93,27 @@ if (app.get('env') === 'development') {
log.error(err);
});
app.use(function(err, req, res, next) {
- if (err instanceof AppError.AppError) {
- res.send(err);
- log.debug(err);
- } else {
- res.status(err.status || 500);
- res.render('error', {
- message: err.message,
- error: err
- });
- log.error(err);
- }
+ res.status(err.status || 500);
+ res.render('error', {
+ message: err.message,
+ error: err
+ });
+ log.error(err);
});
} else {
app.use(function(req, res, next) {
var e = new AppError.NotFound();
- res.status(404).send(e);
+ res.status(404).send(e.message);
log.debug(e);
});
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
if (err instanceof AppError.AppError) {
- res.send(err);
+ res.send(err.message);
+ log.debug(err);
} else {
- var status = err.status || 500;
- var error = new AppError.AppError(`服务器繁忙,请稍后再试!`);
- error.status = status;
- res.status(status).send(error);
+ res.status(err.status || 500).send(err.message);
log.error(err);
}
});
diff --git a/appveyor.yml b/appveyor.yml
index 3fca1216..977b5a1e 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -3,11 +3,7 @@ init:
environment:
matrix:
- - nodejs_version: "7.5"
- - nodejs_version: "7"
- - nodejs_version: "6.9.5"
- - nodejs_version: "6.1"
- - nodejs_version: "6"
+ - nodejs_version: "8.10.0"
services:
- mysql
@@ -27,4 +23,4 @@ test_script:
- set CONFIG_FILE=config/config.testwin.js
- node_modules\.bin\npm run test-win
build: off
-version: "{build}"
\ No newline at end of file
+version: "{build}"
diff --git a/bin/db b/bin/db
index a856921f..005b8810 100755
--- a/bin/db
+++ b/bin/db
@@ -6,8 +6,10 @@
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
-var mysql = require('mysql');
+var mysql = require('mysql2');
var Promise = require("bluebird");
+var common = require("../core/utils/common");
+var constConfig = require('../core/const');
var yargs = require('yargs')
.usage('Usage: $0 [options]')
.command('init', '初始化数据库', {
@@ -76,37 +78,50 @@ if (command === 'init') {
if(connection2) connection2.end()
});
} else if (command == 'upgrade'){
- var connection = mysql.createConnection({
- host: dbhost,
- user: dbuser,
- password: dbpassword,
- database: dbname,
- multipleStatements: true,
- port: dbport
- });
- Promise.promisifyAll(connection);
- connection.connect();
+ try {
+ var connection = mysql.createConnection({
+ host: dbhost,
+ user: dbuser,
+ password: dbpassword,
+ database: dbname,
+ multipleStatements: true,
+ port: dbport
+ });
+ Promise.promisifyAll(connection);
+ connection.connect()
+ } catch(e) {
+ console.error('connect mysql error, check params',e);
+ return;
+ }
+
return Promise.coroutine(function*(val){
var version_no = '0.0.1';
- try {
- var rs = yield connection.queryAsync('select `version` from `versions` where `type`=1 limit 1');
- version_no = _.get(rs,'0.version', '0.0.1');
- } catch (e) {
- }
- if (version_no == '0.2.15') {
+ var rs = yield connection.queryAsync('select `version` from `versions` where `type`=1 limit 1');
+ version_no = _.get(rs,'0.version', '0.0.1');
+ if (version_no == constConfig.CURRENT_DB_VERSION) {
console.log('Everything up-to-date.');
process.exit(0);
}
var allSqlFile = [
- {version:'0.2.14', 'path':path.resolve(__dirname, '../sql/codepush-v0.2.14.sql')},
- {version:'0.2.15', 'path':path.resolve(__dirname, '../sql/codepush-v0.2.15.sql')}
+ {version:'0.2.14', 'path':path.resolve(__dirname, '../sql/codepush-v0.2.14-patch.sql')},
+ {version:'0.2.15', 'path':path.resolve(__dirname, '../sql/codepush-v0.2.15-patch.sql')},
+ {version:'0.3.0', 'path':path.resolve(__dirname, '../sql/codepush-v0.3.0-patch.sql')},
+ {version:'0.4.0', 'path':path.resolve(__dirname, '../sql/codepush-v0.4.0-patch.sql')},
+ {version:'0.5.0', 'path':path.resolve(__dirname, '../sql/codepush-v0.5.0-patch.sql')}
];
for (var i = 0; i < allSqlFile.length; i++) {
if(!_.gt(allSqlFile[i]['version'], version_no)) {
continue;
}
- var sql = fs.readFileSync(allSqlFile[i]['path'], 'utf-8');
- yield connection.queryAsync(sql);
+ try {
+ var sql = fs.readFileSync(allSqlFile[i]['path'], 'utf-8');
+ console.log('exec sql file:' + allSqlFile[i]['path']);
+ yield connection.queryAsync(sql);
+ console.log('success exec sql file:' + allSqlFile[i]['path']);
+ } catch (e) {
+ console.error('error exec sql file:' + allSqlFile[i]['path']);
+ throw e;
+ }
}
})()
.then(function(){
diff --git a/bin/www b/bin/www
index a34f74be..4f086a7b 100755
--- a/bin/www
+++ b/bin/www
@@ -9,7 +9,11 @@ var http = require('http');
var validator = require('validator')
var _ = require('lodash')
var config = require('../core/config');
-log4js.configure(_.get(config, 'log4js', {appenders: [{ type: 'console'}], levels : {"[all]": "ERROR", http: "ERROR"} }));
+var constConfig = require('../core/const');
+log4js.configure(_.get(config, 'log4js', {
+ appenders: {console: { type: 'console'}},
+ categories : { default: { appenders: ['console'], level: 'info' }}
+}));
var log = log4js.getLogger("startup")
var app = require('../app');
@@ -45,8 +49,8 @@ var server = http.createServer(app);
var models = require('../models');
models.Versions.findOne({where:{type:1}})
.then(function(v){
- if (!v || v.get('version') != '0.2.15') {
- throw new Error(`Please upgrade your database. usage bin/db upgrade or code-push-server-db upgrade`);
+ if (!v || v.get('version') != constConfig.CURRENT_DB_VERSION) {
+ throw new Error('Please upgrade your database. usage `npm run upgrade` or `code-push-server-db upgrade`');
}
server.listen(port, host);
server.on('error', onError);
diff --git a/config/config.js b/config/config.js
index 79c4638b..0789b5ee 100644
--- a/config/config.js
+++ b/config/config.js
@@ -1,3 +1,5 @@
+var os = require('os');
+
var config = {};
config.development = {
// Config for database, only support mysql.
@@ -8,7 +10,8 @@ config.development = {
host: process.env.RDS_HOST || "127.0.0.1",
port: process.env.RDS_PORT || 3306,
dialect: "mysql",
- logging: false
+ logging: false,
+ operatorsAliases: false,
},
// Config for qiniu (http://www.qiniu.com/) cloud storage when storageType value is "qiniu".
qiniu: {
@@ -17,6 +20,14 @@ config.development = {
bucketName: "",
downloadUrl: "" // Binary files download host address.
},
+ // Config for upyun (https://www.upyun.com/) storage when storageType value is "upyun"
+ upyun: {
+ storageDir: process.env.UPYUN_STORAGE_DIR,
+ serviceName: process.env.UPYUN_SERVICE_NAME,
+ operatorName: process.env.UPYUN_OPERATOR_NAME,
+ operatorPass: process.env.UPYUN_OPERATOR_PASS,
+ downloadUrl: process.env.DOWNLOAD_URL,
+ },
// Config for Amazon s3 (https://aws.amazon.com/cn/s3/) storage when storageType value is "s3".
s3: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
@@ -35,14 +46,22 @@ config.development = {
prefix: "", // Key prefix in object key
downloadUrl: "", // binary files download host address.
},
+ // Config for tencentyun COS (https://cloud.tencent.com/product/cos) when storageType value is "oss".
+ tencentcloud: {
+ accessKeyId: "",
+ secretAccessKey: "",
+ bucketName: "",
+ region: "",
+ downloadUrl: "", // binary files download host address.
+ },
// Config for local storage when storageType value is "local".
local: {
// Binary files storage dir, Do not use tmpdir and it's public download dir.
storageDir: process.env.STORAGE_DIR || "/Users/tablee/workspaces/storage",
// Binary files download host address which Code Push Server listen to. the files storage in storageDir.
- downloadUrl: process.env.LOCAL_DOWNLOAD_URL || "http://localhost:3000/download",
+ downloadUrl: process.env.LOCAL_DOWNLOAD_URL || "http://127.0.0.1:3000/download",
// public static download spacename.
- public: process.env.PUBLIC || '/download'
+ public: '/download'
},
jwt: {
// Recommended: 63 random alpha-numeric characters
@@ -57,19 +76,21 @@ config.development = {
*/
tryLoginTimes: 0,
// CodePush Web(https://github.com/lisong/code-push-web) login address.
- //codePushWebUrl: "http://localhost:3001/login",
+ //codePushWebUrl: "http://127.0.0.1:3001/login",
// create patch updates's number. default value is 3
diffNums: 3,
// data dir for caclulate diff files. it's optimization.
- dataDir: process.env.DATA_DIR || "/Users/tablee/workspaces/data",
- // storageType which is your binary package files store. options value is ("local" | "qiniu" | "s3")
+ dataDir: process.env.DATA_DIR || os.tmpdir(),
+ // storageType which is your binary package files store. options value is ("local" | "qiniu" | "s3"| "oss" || "tencentcloud")
storageType: process.env.STORAGE_TYPE || "local",
// options value is (true | false), when it's true, it will cache updateCheck results in redis.
- updateCheckCache: false
+ updateCheckCache: false,
+ // options value is (true | false), when it's true, it will cache rollout results in redis
+ rolloutClientUniqueIdCache: false,
},
// Config for smtp email,register module need validate user email project source https://github.com/nodemailer/nodemailer
smtpConfig:{
- host: "smtp.mxhichina.com",
+ host: "smtp.aliyun.com",
port: 465,
secure: true,
auth: {
@@ -103,13 +124,11 @@ config.development = {
}
config.development.log4js = {
- appenders: [
- { type: 'console'}
- ],
- levels : {
- "[all]": "ERROR",
- "startup": "INFO",
- "http" : "INFO"
+ appenders: {console: { type: 'console'}},
+ categories : {
+ "default": { appenders: ['console'], level:'error'},
+ "startup": { appenders: ['console'], level:'info'},
+ "http": { appenders: ['console'], level:'info'}
}
}
diff --git a/config/config.test.js b/config/config.test.js
index fbfd4cd5..22990cff 100644
--- a/config/config.test.js
+++ b/config/config.test.js
@@ -9,11 +9,12 @@ config.test = {
host: "127.0.0.1",
port: 3306,
dialect: "mysql",
- logging: false
+ logging: false,
+ operatorsAliases: false,
},
local: {
storageDir: os.tmpdir(),
- downloadUrl: "http://localhost:3000/download",
+ downloadUrl: "http://127.0.0.1:3000/download",
public: '/download'
},
jwt: {
@@ -24,7 +25,8 @@ config.test = {
diffNums: 3,
dataDir: os.tmpdir(),
storageType: "local",
- updateCheckCache: true
+ updateCheckCache: true,
+ rolloutClientUniqueIdCache: false,
},
smtpConfig: false,
redis: {
@@ -47,13 +49,11 @@ config.test = {
}
}
config.test.log4js = {
- appenders: [
- { type: 'console'}
- ],
- levels : {
- "[all]": "ERROR",
- "startup": "INFO",
- "http" : "INFO"
+ appenders: {console: { type: 'console'}},
+ categories : {
+ "default": { appenders: ['console'], level:'error'},
+ "startup": { appenders: ['console'], level:'info'},
+ "http": { appenders: ['console'], level:'info'}
}
}
module.exports = config;
diff --git a/config/config.testwin.js b/config/config.testwin.js
index 2f6b1f82..2f5fa96d 100644
--- a/config/config.testwin.js
+++ b/config/config.testwin.js
@@ -9,11 +9,12 @@ config.test = {
host: "127.0.0.1",
port: 3306,
dialect: "mysql",
- logging: false
+ logging: false,
+ operatorsAliases: false,
},
local: {
storageDir: os.tmpdir(),
- downloadUrl: "http://localhost:3000/download",
+ downloadUrl: "http://127.0.0.1:3000/download",
public: '/download'
},
jwt: {
@@ -24,7 +25,8 @@ config.test = {
diffNums: 3,
dataDir: os.tmpdir(),
storageType: "local",
- updateCheckCache: true
+ updateCheckCache: true,
+ rolloutClientUniqueIdCache: false,
},
smtpConfig: false,
redis: {
@@ -48,13 +50,11 @@ config.test = {
}
}
config.test.log4js = {
- appenders: [
- { type: 'console'}
- ],
- levels : {
- "[all]": "ERROR",
- "startup": "INFO",
- "http" : "INFO"
+ appenders: {console: { type: 'console'}},
+ categories : {
+ "default": { appenders: ['console'], level:'error'},
+ "startup": { appenders: ['console'], level:'info'},
+ "http": { appenders: ['console'], level:'info'}
}
}
module.exports = config;
diff --git a/core/config.js b/core/config.js
index 6c8f71a7..a67eae86 100644
--- a/core/config.js
+++ b/core/config.js
@@ -1,15 +1,17 @@
-var env = process.env.NODE_ENV || 'development';
+var env = process.env.NODE_ENV || 'development';
var _ = require('lodash');
var path = require('path');
-var config = {};
var log4js = require('log4js');
var log = log4js.getLogger("cps:config");
var CONFIG_PATH = path.join(__dirname, '../config/config.js');
if (process.env.CONFIG_FILE) {
CONFIG_PATH = path.join(__dirname, path.relative(__dirname, process.env.CONFIG_FILE));
- log.info(`process.env.CONFIG_FILE value: ` +process.env.CONFIG_FILE)
+ log.info(`process.env.CONFIG_FILE value: ${process.env.CONFIG_FILE}`)
}
log.info(`use config file ${CONFIG_PATH}`)
log.info(`use env ${env}`)
-config = _.get(require(CONFIG_PATH), env);
+var config = _.get(require(CONFIG_PATH), env);
+if (_.isEmpty(config)) {
+ throw new Error(`config is {}, check the env and config`);
+}
module.exports = config;
diff --git a/core/const.js b/core/const.js
new file mode 100644
index 00000000..f1d9b23b
--- /dev/null
+++ b/core/const.js
@@ -0,0 +1,49 @@
+function define(name, value) {
+ Object.defineProperty(exports, name, {
+ value: value,
+ enumerable: true
+ });
+}
+
+//定义支持的平台
+define("IOS", 1);
+define("IOS_NAME", 'iOS');
+define("ANDROID", 2);
+define("ANDROID_NAME", 'Android');
+define("WINDOWS", 3);
+define("WINDOWS_NAME", 'Windows');
+
+//定义支持的应用类型
+define("REACT_NATIVE", 1);
+define("REACT_NATIVE_NAME", 'React-Native');
+define("CORDOVA", 2);
+define("CORDOVA_NAME", 'Cordova');
+
+define("PRODUCTION", 'Production');
+define("STAGING", 'Staging');
+
+
+define("IS_MANDATORY_YES", 1);
+define("IS_MANDATORY_NO", 0);
+
+
+define("IS_DISABLED_YES", 1);
+define("IS_DISABLED_NO", 0);
+
+
+define("RELEAS_EMETHOD_PROMOTE", 'Promote');
+define("RELEAS_EMETHOD_UPLOAD", 'Upload');
+
+define("DEPLOYMENT_SUCCEEDED", 1);
+define("DEPLOYMENT_FAILED", 2);
+
+define("DIFF_MANIFEST_FILE_NAME", 'hotcodepush.json');
+
+//文本文件是否使用google diff-match-patch 计算差异
+define("IS_USE_DIFF_TEXT_NO", 0);
+define("IS_USE_DIFF_TEXT_YES", 1);
+
+
+define("CURRENT_DB_VERSION", '0.5.0');
+
+
diff --git a/core/middleware.js b/core/middleware.js
index da4676a2..fc4054f2 100644
--- a/core/middleware.js
+++ b/core/middleware.js
@@ -17,8 +17,9 @@ var checkAuthToken = function (authToken) {
if (_.isEmpty(users)) {
throw new AppError.Unauthorized();
}
+ var Sequelize = require('sequelize');
return models.UserTokens.findOne({
- where: {tokens: authToken, uid: users.id, expires_at: { gt: moment().format('YYYY-MM-DD HH:mm:ss') }}
+ where: {tokens: authToken, uid: users.id, expires_at: { [Sequelize.Op.gt]: moment().format('YYYY-MM-DD HH:mm:ss') }}
})
.then((tokenInfo) => {
if (_.isEmpty(tokenInfo)){
@@ -34,7 +35,7 @@ var checkAuthToken = function (authToken) {
var checkAccessToken = function (accessToken) {
return new Promise((resolve, reject) => {
if (_.isEmpty(accessToken)) {
- throw new AppError.Unauthorized();
+ return reject(new AppError.Unauthorized());
}
var config = require('../core/config');
var tokenSecret = _.get(config, 'jwt.tokenSecret');
@@ -42,7 +43,7 @@ var checkAccessToken = function (accessToken) {
try {
var authData = jwt.verify(accessToken, tokenSecret);
} catch (e) {
- reject(new AppError.Unauthorized());
+ return reject(new AppError.Unauthorized());
}
var uid = _.get(authData, 'uid', null);
var hash = _.get(authData, 'hash', null);
@@ -73,18 +74,19 @@ middleware.checkToken = function(req, res, next) {
var authType = 1;
var authToken = null;
if (_.eq(authArr[0], 'Bearer')) {
- authType = 1;
authToken = authArr[1]; //Bearer
+ if (authToken && authToken.length > 64) {
+ authType = 2;
+ } else {
+ authType = 1;
+ }
} else if(_.eq(authArr[0], 'Basic')) {
authType = 2;
var b = new Buffer(authArr[1], 'base64');
var user = _.split(b.toString(), ':');
authToken = _.get(user, '1');
- } else {
- authType = 2;
- authToken = _.trim(_.trimStart(_.get(req, 'query.access_token', null)));
}
- if (authType == 1) {
+ if (authToken && authType == 1) {
checkAuthToken(authToken)
.then((users) => {
req.users = users;
@@ -98,7 +100,7 @@ middleware.checkToken = function(req, res, next) {
next(e);
}
});
- } else if (authType == 2) {
+ } else if (authToken && authType == 2) {
checkAccessToken(authToken)
.then((users) => {
req.users = users;
diff --git a/core/services/account-manager.js b/core/services/account-manager.js
index dd4a06ef..0eae4a9e 100644
--- a/core/services/account-manager.js
+++ b/core/services/account-manager.js
@@ -67,13 +67,11 @@ proto.getAllAccessKeyByUid = function (uid) {
.then((tokens) => {
return _.map(tokens, function(v){
return {
- id: v.id + "",
name: '(hidden)',
createdTime: parseInt(moment(v.created_at).format('x')),
createdBy: v.created_by,
expires: parseInt(moment(v.expires_at).format('x')),
friendlyName: v.name,
- isSession: v.is_session == 0 ? false : true,
description: v.description,
};
});
@@ -86,13 +84,12 @@ proto.isExsitAccessKeyName = function (uid, friendlyName) {
});
};
-proto.createAccessKey = function (uid, newAccessKey, isSession, ttl, friendlyName, createdBy, description) {
+proto.createAccessKey = function (uid, newAccessKey, ttl, friendlyName, createdBy, description) {
return models.UserTokens.create({
uid: uid,
name: friendlyName,
tokens: newAccessKey,
description: description,
- is_session: isSession ? 1 : 0,
created_by: createdBy,
expires_at: moment().add(ttl/1000, 'seconds').format('YYYY-MM-DD HH:mm:ss'),
created_at: moment().format('YYYY-MM-DD HH:mm:ss'),
@@ -132,7 +129,8 @@ proto.login = function (account, password) {
throw new AppError.AppError(`您输入密码错误次数超过限制,帐户已经锁定`);
}
return users;
- });
+ })
+ .finally(() => client.quit());
} else {
return users;
}
@@ -152,7 +150,8 @@ proto.login = function (account, password) {
})
.then(() => {
return client.incrAsync(loginKey);
- });
+ })
+ .finally(() => client.quit());
}
throw new AppError.AppError("您输入的邮箱或密码有误");
} else {
@@ -178,10 +177,12 @@ proto.sendRegisterCode = function (email) {
.then(() => {
//将token临时存储到redis
var token = security.randToken(40);
- return factory.getRedisClient("default").setexAsync(`${REGISTER_CODE}${security.md5(email)}`, EXPIRED, token)
+ var client = factory.getRedisClient("default");
+ return client.setexAsync(`${REGISTER_CODE}${security.md5(email)}`, EXPIRED, token)
.then(() => {
return token;
- });
+ })
+ .finally(() => client.quit());
})
.then((token) => {
//将token发送到用户邮箱
@@ -213,6 +214,7 @@ proto.checkRegisterCode = function (email, token) {
}
return ttl;
})
+ .finally(() => client.quit());
throw new AppError.AppError(`您输入的验证码不正确,请重新输入`);
}
return storageToken;
diff --git a/core/services/app-manager.js b/core/services/app-manager.js
index 0d484b49..b3a56811 100644
--- a/core/services/app-manager.js
+++ b/core/services/app-manager.js
@@ -17,21 +17,24 @@ proto.findAppByName = function (uid, appName) {
return models.Apps.findOne({where: {name: appName, uid: uid}});
};
-proto.addApp = function (uid, appName, identical) {
+proto.addApp = function (uid, appName, os, platform, identical) {
return models.sequelize.transaction((t) => {
return models.Apps.create({
name: appName,
- uid: uid
+ uid: uid,
+ os: os,
+ platform: platform
},{
transaction: t
})
.then((apps) => {
+ var constName = require('../const');
var appId = apps.id;
var deployments = [];
var deploymentKey = security.randToken(28) + identical;
deployments.push({
appid: appId,
- name: 'Production',
+ name: constName.PRODUCTION,
last_deployment_version_id: 0,
label_id: 0,
deployment_key: deploymentKey
@@ -39,7 +42,7 @@ proto.addApp = function (uid, appName, identical) {
deploymentKey = security.randToken(28) + identical;
deployments.push({
appid: appId,
- name: 'Staging',
+ name: constName.STAGING,
last_deployment_version_id: 0,
label_id: 0,
deployment_key: deploymentKey
@@ -91,13 +94,27 @@ proto.listApps = function (uid) {
return [];
} else {
var appIds = _.map(data, (v) => { return v.appid });
- return models.Apps.findAll({where: {id: {in: appIds}}});
+ var Sequelize = require('sequelize');
+ return models.Apps.findAll({where: {id: {[Sequelize.Op.in]: appIds}}});
}
})
.then((appInfos) => {
var rs = Promise.map(_.values(appInfos), (v) => {
return self.getAppDetailInfo(v, uid)
.then((info) => {
+ var constName = require('../const');
+ if (info.os == constName.IOS) {
+ info.os = constName.IOS_NAME;
+ } else if (info.os == constName.ANDROID) {
+ info.os = constName.ANDROID_NAME;
+ } else if (info.os == constName.WINDOWS) {
+ info.os = constName.WINDOWS_NAME;
+ }
+ if (info.platform == constName.REACT_NATIVE) {
+ info.platform = constName.REACT_NATIVE_NAME;
+ } else if (info.platform == constName.CORDOVA) {
+ info.platform = constName.CORDOVA_NAME;
+ }
return info;
});
});
@@ -128,8 +145,10 @@ proto.getAppDetailInfo = function (appInfo, currentUid) {
deployments: _.map(deploymentInfos, (item) => {
return _.get(item, 'name');
}),
-
- name: appInfo.get('name')
+ os: appInfo.get('os'),
+ platform: appInfo.get('platform'),
+ name: appInfo.get('name'),
+ id: appInfo.get('id')
});
});
};
diff --git a/core/services/client-manager.js b/core/services/client-manager.js
index da2dfdea..87a16625 100644
--- a/core/services/client-manager.js
+++ b/core/services/client-manager.js
@@ -5,6 +5,10 @@ var _ = require('lodash');
var common = require('../utils/common');
var factory = require('../utils/factory');
var AppError = require('../app-error');
+var config = require('../config');
+var log4js = require('log4js');
+var log = log4js.getLogger("cps:ClientManager");
+var Sequelize = require('sequelize');
var proto = module.exports = function (){
function ClientManager() {
@@ -15,6 +19,7 @@ var proto = module.exports = function (){
};
const UPDATE_CHECK = "UPDATE_CHECK";
+const CHOSEN_MAN = "CHOSEN_MAN";
const EXPIRED = 600;
proto.getUpdateCheckCacheKey = function(deploymentKey, appVersion, label, packageHash) {
@@ -22,6 +27,7 @@ proto.getUpdateCheckCacheKey = function(deploymentKey, appVersion, label, packag
}
proto.clearUpdateCheckCache = function(deploymentKey, appVersion, label, packageHash) {
+ log.debug('clear cache Deployments key:', deploymentKey);
let redisCacheKey = this.getUpdateCheckCacheKey(deploymentKey, appVersion, label, packageHash);
var client = factory.getRedisClient("default");
return client.keysAsync(redisCacheKey)
@@ -32,12 +38,13 @@ proto.clearUpdateCheckCache = function(deploymentKey, appVersion, label, package
});
}
return null;
- });
+ })
+ .finally(() => client.quit());
}
-proto.updateCheckFromCache = function(deploymentKey, appVersion, label, packageHash) {
+proto.updateCheckFromCache = function(deploymentKey, appVersion, label, packageHash, clientUniqueId) {
const self = this;
- var updateCheckCache = _.get(require('../config'), 'common.updateCheckCache', false);
+ var updateCheckCache = _.get(config, 'common.updateCheckCache', false);
if (updateCheckCache === false) {
return self.updateCheck(deploymentKey, appVersion, label, packageHash);
}
@@ -47,14 +54,16 @@ proto.updateCheckFromCache = function(deploymentKey, appVersion, label, packageH
.then((data) => {
if (data) {
try {
+ log.debug('updateCheckFromCache read from catch');
var obj = JSON.parse(data);
return obj;
} catch (e) {
}
}
- return self.updateCheck(deploymentKey, appVersion, label, packageHash)
+ return self.updateCheck(deploymentKey, appVersion, label, packageHash, clientUniqueId)
.then((rs) => {
try {
+ log.debug('updateCheckFromCache read from db');
var strRs = JSON.stringify(rs);
client.setexAsync(redisCacheKey, EXPIRED, strRs);
} catch (e) {
@@ -62,30 +71,103 @@ proto.updateCheckFromCache = function(deploymentKey, appVersion, label, packageH
return rs;
});
})
+ .finally(() => client.quit());
+}
+
+proto.getChosenManCacheKey = function(packageId, rollout, clientUniqueId) {
+ return [CHOSEN_MAN, packageId, rollout, clientUniqueId].join(':');
+}
+
+proto.random = function(rollout) {
+ var r = Math.ceil(Math.random()*10000);
+ if (r < rollout * 100) {
+ return Promise.resolve(true);
+ } else {
+ return Promise.resolve(false);
+ }
}
-proto.updateCheck = function(deploymentKey, appVersion, label, packageHash) {
+proto.chosenMan = function (packageId, rollout, clientUniqueId) {
+ var self = this;
+ if (rollout >= 100) {
+ return Promise.resolve(true);
+ }
+ var rolloutClientUniqueIdCache = _.get(config, 'common.rolloutClientUniqueIdCache', false);
+ if (rolloutClientUniqueIdCache === false) {
+ return self.random(rollout);
+ } else {
+ var client = factory.getRedisClient("default");
+ var redisCacheKey = self.getChosenManCacheKey(packageId, rollout, clientUniqueId);
+ return client.getAsync(redisCacheKey)
+ .then((data) => {
+ if (data == 1) {
+ return true;
+ } else if (data == 2) {
+ return false;
+ } else {
+ return self.random(rollout)
+ .then((r)=>{
+ return client.setexAsync(redisCacheKey, 60*60*24*7, r ? 1:2)
+ .then(()=>{
+ return r;
+ });
+ });
+ }
+ })
+ .finally(() => client.quit());
+ }
+}
+
+proto.updateCheck = function(deploymentKey, appVersion, label, packageHash, clientUniqueId) {
var rs = {
+ packageId: 0,
downloadURL: "",
+ downloadUrl: "",
description: "",
isAvailable: false,
+ isDisabled: true,
isMandatory: false,
appVersion: appVersion,
+ targetBinaryRange: "",
packageHash: "",
label: "",
packageSize: 0,
updateAppVersion: false,
- shouldRunBinaryVersion: false
+ shouldRunBinaryVersion: false,
+ rollout: 100
};
+ var self = this;
if (_.isEmpty(deploymentKey) || _.isEmpty(appVersion)) {
return Promise.reject(new AppError.AppError("please input deploymentKey and appVersion"))
}
return models.Deployments.findOne({where: {deployment_key: deploymentKey}})
.then((dep) => {
if (_.isEmpty(dep)) {
- throw new AppError.AppError('does not found deployment');
+ throw new AppError.AppError('Not found deployment, check deployment key is right.');
}
- return models.DeploymentsVersions.findOne({where: {deployment_id: dep.id, app_version: appVersion}});
+ var version = common.parseVersion(appVersion);
+ return models.DeploymentsVersions.findAll({where: {
+ deployment_id: dep.id,
+ min_version: { [Sequelize.Op.lte]: version },
+ max_version: { [Sequelize.Op.gt]: version }
+ }})
+ .then((deploymentsVersionsMore) => {
+ var distance = 0;
+ var item = null;
+ _.map(deploymentsVersionsMore, function(value, index) {
+ if (index == 0) {
+ item = value;
+ distance = value.max_version - value.min_version;
+ } else {
+ if (distance > (value.max_version - value.min_version)) {
+ distance = value.max_version - value.min_version;
+ item = value;
+ }
+ }
+ });
+ log.debug(item);
+ return item;
+ });
})
.then((deploymentsVersions) => {
var packageId = _.get(deploymentsVersions, 'current_package_id', 0);
@@ -97,26 +179,29 @@ proto.updateCheck = function(deploymentKey, appVersion, label, packageHash) {
if (packages
&& _.eq(packages.deployment_id, deploymentsVersions.deployment_id)
&& !_.eq(packages.package_hash, packageHash)) {
- rs.downloadURL = common.getBlobDownloadUrl(_.get(packages, 'blob_url'));
+ rs.packageId = packageId;
+ rs.targetBinaryRange = deploymentsVersions.app_version;
+ rs.downloadUrl = rs.downloadURL = common.getBlobDownloadUrl(_.get(packages, 'blob_url'));
rs.description = _.get(packages, 'description', '');
- rs.isAvailable = true;
+ rs.isAvailable = _.eq(packages.is_disabled, 1) ? false : true;
+ rs.isDisabled = _.eq(packages.is_disabled, 1) ? true : false;
rs.isMandatory = _.eq(packages.is_mandatory, 1) ? true : false;
rs.appVersion = appVersion;
rs.packageHash = _.get(packages, 'package_hash', '');
rs.label = _.get(packages, 'label', '');
rs.packageSize = _.get(packages, 'size', 0);
- rs.shouldRunBinaryVersion = false;
+ rs.rollout = _.get(packages, 'rollout', 100);
}
return packages;
})
-
.then((packages) => {
- //差异化更新
+ //增量更新
if (!_.isEmpty(packages) && !_.eq(_.get(packages, 'package_hash', ""), packageHash)) {
return models.PackagesDiff.findOne({where: {package_id:packages.id, diff_against_package_hash: packageHash}})
.then((diffPackage) => {
if (!_.isEmpty(diffPackage)) {
rs.downloadURL = common.getBlobDownloadUrl(_.get(diffPackage, 'diff_blob_url'));
+ rs.downloadUrl = common.getBlobDownloadUrl(_.get(diffPackage, 'diff_blob_url'));
rs.packageSize = _.get(diffPackage, 'diff_size', 0);
}
return;
@@ -153,26 +238,82 @@ proto.getPackagesInfo = function (deploymentKey, label) {
proto.reportStatusDownload = function(deploymentKey, label, clientUniqueId) {
return this.getPackagesInfo(deploymentKey, label)
.then((packages) => {
- return models.PackagesMetrics.addOneOnDownloadById(packages.id);
+ return Promise.all([
+ models.PackagesMetrics.findOne({where: {package_id: packages.id}})
+ .then((metrics)=>{
+ if (metrics) {
+ return metrics.increment('downloaded');
+ }
+ return;
+ }),
+ models.LogReportDownload.create({
+ package_id: packages.id,
+ client_unique_id: clientUniqueId
+ })
+ ]);
});
};
proto.reportStatusDeploy = function (deploymentKey, label, clientUniqueId, others) {
return this.getPackagesInfo(deploymentKey, label)
.then((packages) => {
- var status = _.get(others, "status");
+ var constConfig = require('../const');
+ var statusText = _.get(others, "status");
+ var status = 0;
+ if (_.eq(statusText, "DeploymentSucceeded")) {
+ status = constConfig.DEPLOYMENT_SUCCEEDED;
+ } else if (_.eq(statusText, "DeploymentFailed")) {
+ status = constConfig.DEPLOYMENT_FAILED;
+ }
var packageId = packages.id;
- if (_.eq(status, "DeploymentSucceeded")) {
- return Promise.all([
- models.PackagesMetrics.addOneOnInstalledById(packageId),
- models.PackagesMetrics.addOneOnActiveById(packageId),
- ]);
- } else if (_.eq(status, "DeploymentFailed")) {
+ var previous_deployment_key = _.get(others, 'previousDeploymentKey');
+ var previous_label = _.get(others, 'previousLabelOrAppVersion');
+ if (status > 0) {
return Promise.all([
- models.PackagesMetrics.addOneOnInstalledById(packageId),
- models.PackagesMetrics.addOneOnFailedById(packageId)
- ]);
- }else {
+ models.LogReportDeploy.create({
+ package_id: packageId,
+ client_unique_id: clientUniqueId,
+ previous_label: previous_label,
+ previous_deployment_key: previous_deployment_key,
+ status: status
+ }),
+ models.PackagesMetrics.findOne({where: {package_id: packageId}})
+ .then((metrics)=>{
+ if (_.isEmpty(metrics)) {
+ return;
+ }
+ if (_.eq(status, constConfig.DEPLOYMENT_SUCCEEDED)) {
+ return metrics.increment(['installed', 'active'],{by: 1});
+ } else {
+ return metrics.increment(['installed', 'failed'],{by: 1});
+ }
+ })
+ ])
+ .then(()=>{
+ if (previous_deployment_key && previous_label) {
+ return models.Deployments.findOne({where: {deployment_key: previous_deployment_key}})
+ .then((dep)=>{
+ if (_.isEmpty(dep)) {
+ return;
+ }
+ return models.Packages.findOne({where: {deployment_id: dep.id, label: previous_label}})
+ .then((p)=>{
+ if (_.isEmpty(p)) {
+ return;
+ }
+ return models.PackagesMetrics.findOne({where:{package_id: p.id}});
+ });
+ })
+ .then((metrics)=>{
+ if (metrics) {
+ return metrics.decrement('active');
+ }
+ return;
+ });
+ }
+ return;
+ });
+ } else {
return;
}
});
diff --git a/core/services/collaborators.js b/core/services/collaborators.js
index 13241cca..b2371103 100644
--- a/core/services/collaborators.js
+++ b/core/services/collaborators.js
@@ -21,7 +21,8 @@ proto.listCollaborators = function (appId) {
}, []);
})
.then((coInfo) => {
- return models.Users.findAll({where: {id: {in: coInfo.uids}}})
+ var Sequelize = require('sequelize');
+ return models.Users.findAll({where: {id: {[Sequelize.Op.in]: coInfo.uids}}})
.then((data2) => {
return _.reduce(data2, function (result, value, key) {
var permission = "";
diff --git a/core/services/datacenter-manager.js b/core/services/datacenter-manager.js
index 1193d2ea..c59d29ff 100644
--- a/core/services/datacenter-manager.js
+++ b/core/services/datacenter-manager.js
@@ -11,6 +11,7 @@ const CONTENTS_NAME = 'contents';
var AppError = require('../app-error');
var log4js = require('log4js');
var log = log4js.getLogger("cps:DataCenterManager");
+var path = require('path');
var proto = module.exports = function (){
function DataCenterManager() {
@@ -30,18 +31,18 @@ proto.getDataDir = function () {
proto.hasPackageStoreSync = function (packageHash) {
var dataDir = this.getDataDir();
- var packageHashPath = `${dataDir}/${packageHash}`;
- var manifestFile = `${packageHashPath}/${MANIFEST_FILE_NAME}`;
- var contentPath = `${packageHashPath}/${CONTENTS_NAME}`;
+ var packageHashPath = path.join(dataDir, packageHash);
+ var manifestFile = path.join(packageHashPath, MANIFEST_FILE_NAME);
+ var contentPath = path.join(packageHashPath, CONTENTS_NAME);
return fs.existsSync(manifestFile) && fs.existsSync(contentPath);
}
proto.getPackageInfo = function (packageHash) {
if (this.hasPackageStoreSync(packageHash)){
var dataDir = this.getDataDir();
- var packageHashPath = `${dataDir}/${packageHash}`;
- var manifestFile = `${packageHashPath}/${MANIFEST_FILE_NAME}`;
- var contentPath = `${packageHashPath}/${CONTENTS_NAME}`;
+ var packageHashPath = path.join(dataDir, packageHash);
+ var manifestFile = path.join(packageHashPath, MANIFEST_FILE_NAME);
+ var contentPath = path.join(packageHashPath, CONTENTS_NAME);
return this.buildPackageInfo(packageHash, packageHashPath, contentPath, manifestFile);
} else {
throw new AppError.AppError('can\'t get PackageInfo');
@@ -59,9 +60,9 @@ proto.buildPackageInfo = function (packageHash, packageHashPath, contentPath, ma
proto.validateStore = function (providePackageHash) {
var dataDir = this.getDataDir();
- var packageHashPath = `${dataDir}/${providePackageHash}`;
- var manifestFile = `${packageHashPath}/${MANIFEST_FILE_NAME}`;
- var contentPath = `${packageHashPath}/${CONTENTS_NAME}`;
+ var packageHashPath = path.join(dataDir, providePackageHash);
+ var manifestFile = path.join(packageHashPath, MANIFEST_FILE_NAME);
+ var contentPath = path.join(packageHashPath, CONTENTS_NAME);
if (!this.hasPackageStoreSync(providePackageHash)) {
log.debug(`validateStore providePackageHash not exist`);
return Promise.resolve(false);
@@ -98,9 +99,9 @@ proto.storePackage = function (sourceDst, force) {
var packageHash = security.packageHashSync(manifestJson);
log.debug('storePackage manifestJson packageHash:', packageHash);
var dataDir = self.getDataDir();
- var packageHashPath = `${dataDir}/${packageHash}`;
- var manifestFile = `${packageHashPath}/${MANIFEST_FILE_NAME}`;
- var contentPath = `${packageHashPath}/${CONTENTS_NAME}`;
+ var packageHashPath = path.join(dataDir, packageHash);
+ var manifestFile = path.join(packageHashPath, MANIFEST_FILE_NAME);
+ var contentPath = path.join(packageHashPath, CONTENTS_NAME);
return self.validateStore(packageHash)
.then((isValidate) => {
if (!force && isValidate) {
@@ -109,7 +110,7 @@ proto.storePackage = function (sourceDst, force) {
log.debug(`storePackage cover from sourceDst:`, sourceDst);
return common.createEmptyFolder(packageHashPath)
.then(() => {
- return common.move(sourceDst, contentPath)
+ return common.copy(sourceDst, contentPath)
.then(() => {
var manifestString = JSON.stringify(manifestJson);
fs.writeFileSync(manifestFile, manifestString);
diff --git a/core/services/package-manager.js b/core/services/package-manager.js
index 6d68cda7..fe259c7c 100644
--- a/core/services/package-manager.js
+++ b/core/services/package-manager.js
@@ -12,6 +12,7 @@ var common = require('../utils/common');
var os = require('os');
var path = require('path');
var AppError = require('../app-error');
+var constConfig = require('../const');
var log4js = require('log4js');
var log = log4js.getLogger("cps:PackageManager");
@@ -23,14 +24,31 @@ var proto = module.exports = function (){
return PackageManager;
};
-proto.getMetricsbyPackageId= function(packageId) {
+proto.getMetricsbyPackageId = function(packageId) {
return models.PackagesMetrics.findOne({where: {package_id: packageId}});
}
+proto.findPackageInfoByDeploymentIdAndLabel = function (deploymentId, label) {
+ return models.Packages.findOne({where: {deployment_id: deploymentId, label:label}});
+}
+
+proto.findLatestPackageInfoByDeployVersion = function (deploymentsVersionsId) {
+ return models.DeploymentsVersions.findById(deploymentsVersionsId)
+ .then((deploymentsVersions)=>{
+ if (!deploymentsVersions || deploymentsVersions.current_package_id < 0) {
+ var e = new AppError.AppError("not found last packages");
+ log.debug(e);
+ throw e;
+ }
+ return models.Packages.findById(deploymentsVersions.current_package_id);
+ });
+}
+
proto.parseReqFile = function (req) {
log.debug('parseReqFile');
return new Promise((resolve, reject) => {
var form = new formidable.IncomingForm();
+ form.maxFieldsSize = 200 * 1024 * 1024;
form.parse(req, (err, fields, files) => {
if (err) {
log.debug('parseReqFile:', err);
@@ -50,9 +68,9 @@ proto.parseReqFile = function (req) {
});
};
-proto.createDeploymentsVersionIfNotExist = function (deploymentId, appVersion, t) {
+proto.createDeploymentsVersionIfNotExist = function (deploymentId, appVersion, minVersion, maxVersion, t) {
return models.DeploymentsVersions.findOrCreate({
- where: {deployment_id: deploymentId, app_version: appVersion},
+ where: {deployment_id: deploymentId, app_version: appVersion, min_version:minVersion, max_version:maxVersion},
defaults: {current_package_id: 0},
transaction: t
})
@@ -84,18 +102,20 @@ proto.isMatchPackageHash = function (packageId, packageHash) {
};
proto.createPackage = function (deploymentId, appVersion, packageHash, manifestHash, blobHash, params) {
- var releaseMethod = params.releaseMethod || 'Upload';
+ var releaseMethod = params.releaseMethod || constConfig.RELEAS_EMETHOD_UPLOAD;
var releaseUid = params.releaseUid || 0;
- var isMandatory = params.isMandatory ? 1 : 0;
+ var isMandatory = params.isMandatory || 0;
var size = params.size || 0;
+ var rollout = params.rollout || 100;
var description = params.description || "";
var originalLabel = params.originalLabel || "";
+ var isDisabled = params.isDisabled || 0;
var originalDeployment = params.originalDeployment || "";
var self = this;
return models.Deployments.generateLabelId(deploymentId)
.then((labelId) => {
return models.sequelize.transaction((t) => {
- return self.createDeploymentsVersionIfNotExist(deploymentId, appVersion, t)
+ return self.createDeploymentsVersionIfNotExist(deploymentId, appVersion, params.min_version, params.max_version, t)
.then((deploymentsVersions) => {
return models.Packages.create({
deployment_version_id: deploymentsVersions.id,
@@ -109,6 +129,8 @@ proto.createPackage = function (deploymentId, appVersion, packageHash, manifestH
label: "v" + labelId,
released_by: releaseUid,
is_mandatory: isMandatory,
+ is_disabled: isDisabled,
+ rollout: rollout,
original_label: originalLabel,
original_deployment: originalDeployment
},{transaction: t})
@@ -144,9 +166,9 @@ proto.downloadPackageAndExtract = function (workDirectoryPath, packageHash, blob
return dataCenterManager.getPackageInfo(packageHash);
} else {
var downloadURL = common.getBlobDownloadUrl(blobHash);
- return common.createFileFromRequest(downloadURL, `${workDirectoryPath}/${blobHash}`)
+ return common.createFileFromRequest(downloadURL, path.join(workDirectoryPath, blobHash))
.then((download) => {
- return common.unzipFile(`${workDirectoryPath}/${blobHash}`, `${workDirectoryPath}/current`)
+ return common.unzipFile(path.join(workDirectoryPath, blobHash), path.join(workDirectoryPath, 'current'))
.then((outputPath) => {
return dataCenterManager.storePackage(outputPath, true);
});
@@ -171,14 +193,22 @@ proto.zipDiffPackage = function (fileName, files, baseDirectoryPath, hotCodePush
});
for (var i = 0; i < files.length; ++i) {
var file = files[i];
- zipFile.addFile(`${baseDirectoryPath}/${file}`, slash(file));
+ zipFile.addFile(path.join(baseDirectoryPath, file), slash(file));
}
- zipFile.addFile(hotCodePushFile, 'hotcodepush.json');
+ zipFile.addFile(hotCodePushFile, constConfig.DIFF_MANIFEST_FILE_NAME);
zipFile.end();
});
}
-proto.generateOneDiffPackage = function (workDirectoryPath, packageId, dataCenter, diffPackageHash, diffManifestBlobHash) {
+proto.generateOneDiffPackage = function (
+ workDirectoryPath,
+ packageId,
+ originDataCenter,
+ oldPackageDataCenter,
+ diffPackageHash,
+ diffManifestBlobHash,
+ isUseDiffText
+) {
var self = this;
return models.PackagesDiff.findOne({
where:{
@@ -190,24 +220,53 @@ proto.generateOneDiffPackage = function (workDirectoryPath, packageId, dataCente
if (!_.isEmpty(diffPackage)) {
return;
}
+ log.debug('originDataCenter', originDataCenter);
+ log.debug('oldPackageDataCenter', oldPackageDataCenter);
var downloadURL = common.getBlobDownloadUrl(diffManifestBlobHash);
- return common.createFileFromRequest(downloadURL, `${workDirectoryPath}/${diffManifestBlobHash}`)
+ return common.createFileFromRequest(downloadURL, path.join(workDirectoryPath,diffManifestBlobHash))
.then(() => {
- var originContentPath = dataCenter.contentPath;
- var originManifestJson = JSON.parse(fs.readFileSync(dataCenter.manifestFilePath, "utf8"))
- var diffManifestJson = JSON.parse(fs.readFileSync(`${workDirectoryPath}/${diffManifestBlobHash}`, "utf8"))
+ var dataCenterContentPath = path.join(workDirectoryPath, 'dataCenter');
+ common.copySync(originDataCenter.contentPath, dataCenterContentPath);
+ var oldPackageDataCenterContentPath = oldPackageDataCenter.contentPath;
+ var originManifestJson = JSON.parse(fs.readFileSync(originDataCenter.manifestFilePath, "utf8"))
+ var diffManifestJson = JSON.parse(fs.readFileSync(path.join(workDirectoryPath, diffManifestBlobHash), "utf8"))
var json = common.diffCollectionsSync(originManifestJson, diffManifestJson);
var files = _.concat(json.diff, json.collection1Only);
- var hotcodepush = {deletedFiles: json.collection2Only};
- var hotCodePushFile = `${workDirectoryPath}/${diffManifestBlobHash}_hotcodepush`;
+ var hotcodepush = {deletedFiles: json.collection2Only, patchedFiles:[]};
+ if (isUseDiffText == constConfig.IS_USE_DIFF_TEXT_YES) {
+ //使用google diff-match-patch
+ _.forEach(json.diff, function(tmpFilePath) {
+ var dataCenterContentPathTmpFilePath = path.join(dataCenterContentPath, tmpFilePath);
+ var oldPackageDataCenterContentPathTmpFilePath = path.join(oldPackageDataCenterContentPath, tmpFilePath);
+ if (
+ fs.existsSync(dataCenterContentPathTmpFilePath)
+ && fs.existsSync(oldPackageDataCenterContentPathTmpFilePath)
+ && common.detectIsTextFile(dataCenterContentPathTmpFilePath)
+ && common.detectIsTextFile(oldPackageDataCenterContentPathTmpFilePath)
+ ) {
+ var textOld = fs.readFileSync(oldPackageDataCenterContentPathTmpFilePath, 'utf-8');
+ var textNew = fs.readFileSync(dataCenterContentPathTmpFilePath, 'utf-8');
+ if (!textOld || !textNew) {
+ return;
+ }
+ var DiffMatchPatch = require('diff-match-patch');
+ var dmp = new DiffMatchPatch();
+ var patchs = dmp.patch_make(textOld, textNew);
+ var patchText = dmp.patch_toText(patchs);
+ if (patchText && patchText.length < _.parseInt(textNew.length * 0.8)) {
+ fs.writeFileSync(dataCenterContentPathTmpFilePath, patchText);
+ hotcodepush.patchedFiles.push(tmpFilePath);
+ }
+ }
+ });
+ }
+ var hotCodePushFile = path.join(workDirectoryPath,`${diffManifestBlobHash}_hotcodepush`);;
fs.writeFileSync(hotCodePushFile, JSON.stringify(hotcodepush));
- var fileName = `${workDirectoryPath}/${diffManifestBlobHash}.zip`;
-
- return self.zipDiffPackage(fileName, files, originContentPath, hotCodePushFile)
+ var fileName = path.join(workDirectoryPath,`${diffManifestBlobHash}.zip`);;
+ return self.zipDiffPackage(fileName, files, dataCenterContentPath, hotCodePushFile)
.then((data) => {
return security.qetag(data.path)
.then((diffHash) => {
- log.debug('diff');
return common.uploadFileToStorage(diffHash, fileName)
.then(() => {
var stats = fs.statSync(fileName);
@@ -224,39 +283,36 @@ proto.generateOneDiffPackage = function (workDirectoryPath, packageId, dataCente
});
};
-proto.createDiffPackagesByLastNums = function (packageId, num) {
+proto.createDiffPackagesByLastNums = function (appId, originalPackage, num) {
var self = this;
- return models.Packages.findById(packageId)
- .then((originalPackage) => {
- if (_.isEmpty(originalPackage)) {
- throw AppError.AppError('can\'t find Package');
- }
- return Promise.all([
- models.Packages.findAll({
- where:{
- deployment_version_id: originalPackage.deployment_version_id,
- id: {$lt: packageId}},
- order: [['id','desc']],
- limit: num
- }),
- models.Packages.findAll({
- where:{
- deployment_version_id: originalPackage.deployment_version_id,
- id: {$lt: packageId}},
- order: [['id','asc']],
- limit: 2
- })
- ])
- .spread((lastNumsPackages, basePackages) => {
- return _.unionBy(lastNumsPackages, basePackages, 'id');
- })
- .then((lastNumsPackages) => {
- return self.createDiffPackages(originalPackage, lastNumsPackages);
- });
+ var Sequelize = require('sequelize');
+ var packageId = originalPackage.id;
+ return Promise.all([
+ models.Packages.findAll({
+ where:{
+ deployment_version_id: originalPackage.deployment_version_id,
+ id: {[Sequelize.Op.lt]: packageId}},
+ order: [['id','desc']],
+ limit: num
+ }),
+ models.Packages.findAll({
+ where:{
+ deployment_version_id: originalPackage.deployment_version_id,
+ id: {[Sequelize.Op.lt]: packageId}},
+ order: [['id','asc']],
+ limit: 2
+ }),
+ models.Apps.findById(appId),
+ ])
+ .spread((lastNumsPackages, basePackages, appInfo) => {
+ return [_.uniqBy(_.unionBy(lastNumsPackages, basePackages, 'id'), 'package_hash'), appInfo];
+ })
+ .spread((lastNumsPackages, appInfo) => {
+ return self.createDiffPackages(originalPackage, lastNumsPackages, _.get(appInfo, 'is_use_diff_text', constConfig.IS_USE_DIFF_TEXT_NO));
});
};
-proto.createDiffPackages = function (originalPackage, destPackages) {
+proto.createDiffPackages = function (originalPackage, destPackages, isUseDiffText) {
if (!_.isArray(destPackages)) {
return Promise.reject(new AppError.AppError('第二个参数必须是数组'));
}
@@ -268,60 +324,67 @@ proto.createDiffPackages = function (originalPackage, destPackages) {
var manifest_blob_url = _.get(originalPackage, 'manifest_blob_url');
var blob_url = _.get(originalPackage, 'blob_url');
var workDirectoryPath = path.join(os.tmpdir(), 'codepush_' + security.randToken(32));
+ log.debug('workDirectoryPath', workDirectoryPath);
return common.createEmptyFolder(workDirectoryPath)
.then(() => self.downloadPackageAndExtract(workDirectoryPath, package_hash, blob_url))
- .then((dataCenter) => Promise.map(destPackages,
- (v) => self.generateOneDiffPackage(workDirectoryPath, originalPackage.id, dataCenter, v.package_hash, v.manifest_blob_url)
+ .then((originDataCenter) => Promise.map(destPackages,
+ (v) => {
+ var diffWorkDirectoryPath = path.join(workDirectoryPath, _.get(v, 'package_hash'));
+ common.createEmptyFolderSync(diffWorkDirectoryPath);
+ return self.downloadPackageAndExtract(diffWorkDirectoryPath, _.get(v, 'package_hash'), _.get(v, 'blob_url'))
+ .then((oldPackageDataCenter) =>
+ self.generateOneDiffPackage(
+ diffWorkDirectoryPath,
+ originalPackage.id,
+ originDataCenter,
+ oldPackageDataCenter,
+ v.package_hash,
+ v.manifest_blob_url,
+ isUseDiffText
+ )
+ )
+ }
))
.finally(() => common.deleteFolderSync(workDirectoryPath));
}
-proto.releasePackage = function (deploymentId, packageInfo, fileType, filePath, releaseUid, pubType) {
+proto.releasePackage = function (appId, deploymentId, packageInfo, filePath, releaseUid) {
var self = this;
var appVersion = packageInfo.appVersion;
- if (!/^([0-9.]+)$/.test(appVersion)) {
+ var versionInfo = common.validatorVersion(appVersion);
+ if (!versionInfo[0]) {
log.debug(`releasePackage targetBinaryVersion ${appVersion} not support.`);
return Promise.reject(new AppError.AppError(`targetBinaryVersion ${appVersion} not support.`))
}
- var description = packageInfo.description;
- var isMandatory = packageInfo.isMandatory;
+ var description = packageInfo.description; //描述
+ var isDisabled = packageInfo.isDisabled; //是否立刻下载
+ var rollout = packageInfo.rollout; //灰度百分比
+ var isMandatory = packageInfo.isMandatory; //是否强制更新,无法跳过
var tmpDir = os.tmpdir();
- var directoryPath = path.join(tmpDir, 'codepush_' + security.randToken(32));
+ var directoryPathParent = path.join(tmpDir, 'codepuh_' + security.randToken(32));
+ var directoryPath = path.join(directoryPathParent, 'current');
log.debug(`releasePackage generate an random dir path: ${directoryPath}`);
return Promise.all([
security.qetag(filePath),
common.createEmptyFolder(directoryPath)
.then(() => {
- if (fileType == "application/zip") {
- return common.unzipFile(filePath, directoryPath)
- } else {
- log.debug(`上传的文件格式不对`);
- throw new AppError.AppError("上传的文件格式不对");
- }
+ return common.unzipFile(filePath, directoryPath)
})
])
.spread((blobHash) => {
return security.uploadPackageType(directoryPath)
.then((type) => {
- if (type === 1) {
- //android
- if (pubType == 'ios' ) {
- var e = new AppError.AppError("it must be publish it by ios type");
- log.debug(e);
- throw e;
- }
- } else if (type === 2) {
- //ios
- if (pubType == 'android'){
- var e = new AppError.AppError("it must be publish it by android type");
- log.debug(e);
- throw e;
+ return models.Apps.findById(appId).then((appInfo)=>{
+ if (type > 0 && appInfo.os > 0 && appInfo.os != type) {
+ var e = new AppError.AppError("it must be publish it by ios type");
+ log.debug(e);
+ throw e;
+ } else {
+ //不验证
+ log.debug(`Unknown package type:`, type, ',db os:', appInfo.os);
}
- } else {
- //不验证
- log.debug(`Unknown package type`);
- }
- return blobHash;
+ return blobHash;
+ });
});
})
.then((blobHash) => {
@@ -330,8 +393,11 @@ proto.releasePackage = function (deploymentId, packageInfo, fileType, filePath,
.then((dataCenter) => {
var packageHash = dataCenter.packageHash;
var manifestFile = dataCenter.manifestFilePath;
- return self.createDeploymentsVersionIfNotExist(deploymentId, appVersion)
+ return models.DeploymentsVersions.findOne({where: {deployment_id: deploymentId, app_version:appVersion}})
.then((deploymentsVersions) => {
+ if (!deploymentsVersions) {
+ return false;
+ }
return self.isMatchPackageHash(deploymentsVersions.get('current_package_id'), packageHash);
})
.then((isExist) => {
@@ -354,90 +420,181 @@ proto.releasePackage = function (deploymentId, packageInfo, fileType, filePath,
.spread((packageHash, manifestHash, blobHash) => {
var stats = fs.statSync(filePath);
var params = {
- releaseMethod: 'Upload',
+ releaseMethod: constConfig.RELEAS_EMETHOD_UPLOAD,
releaseUid: releaseUid,
- isMandatory: isMandatory,
+ isMandatory: isMandatory ? constConfig.IS_MANDATORY_YES : constConfig.IS_MANDATORY_NO,
+ isDisabled: isDisabled ? constConfig.IS_DISABLED_YES : constConfig.IS_DISABLED_NO,
+ rollout: rollout,
size: stats.size,
- description: description
+ description: description,
+ min_version: versionInfo[1],
+ max_version: versionInfo[2],
}
return self.createPackage(deploymentId, appVersion, packageHash, manifestHash, blobHash, params);
})
- .finally(() => common.deleteFolderSync(directoryPath))
+ .finally(() => common.deleteFolderSync(directoryPathParent))
};
-proto.modifyReleasePackage = function(deploymentId, deploymentVersionId, packageInfo) {
- var appVersion = _.get(packageInfo, 'appVersion');
- var description = _.get(packageInfo, 'description');
- var isMandatory = _.get(packageInfo, 'isMandatory');
- var isDisabled = _.get(packageInfo, 'isDisabled');
- return models.DeploymentsVersions.findById(deploymentVersionId)
- .then((deploymentsVersions) => {
- if (_.isBoolean(isDisabled)) {
- throw new AppError.AppError(`--disabled -x function is not implements`);
- }
- if (!appVersion) {
- if (!/^([0-9.]+)$/.test(appVersion)) {
- return Promise.reject(new AppError.AppError(`targetBinaryVersion ${appVersion} not support.`))
- }
- return models.DeploymentsVersions.findOne({deployment_id: deploymentId, app_version: appVersion})
- .then((d) => {
- if (d) {
- throw new AppError.AppError(`version ${appVersion} already exist`);
- }
- });
+proto.modifyReleasePackage = function(packageId, params) {
+ var appVersion = _.get(params, 'appVersion');
+ var description = _.get(params, 'description');
+ var isMandatory = _.get(params, 'isMandatory');
+ var isDisabled = _.get(params, 'isDisabled');
+ var rollout = _.get(params, 'rollout');
+ return models.Packages.findById(packageId)
+ .then((packageInfo) => {
+ if (!packageInfo) {
+ throw new AppError.AppError(`packageInfo not found`);
}
- if(!deploymentsVersions) {
- throw new AppError.AppError(`packages were not found in db`);
+ if (!_.isNull(appVersion)) {
+ var versionInfo = common.validatorVersion(appVersion);
+ if (!versionInfo[0]) {
+ throw new AppError.AppError(`--targetBinaryVersion ${appVersion} not support.`);
+ }
+ return Promise.all([
+ models.DeploymentsVersions.findOne({where: {deployment_id:packageInfo.deployment_id, app_version:appVersion}}),
+ models.DeploymentsVersions.findById(packageInfo.deployment_version_id)
+ ])
+ .spread((v1, v2) => {
+ if (v1 && !_.eq(v1.id, v2.id)) {
+ log.debug(v1);
+ throw new AppError.AppError(`${appVersion} already exist.`);
+ }
+ if (!v2) {
+ throw new AppError.AppError(`packages not found.`);
+ }
+ return models.DeploymentsVersions.update({
+ app_version:appVersion,
+ min_version:versionInfo[1],
+ max_version:versionInfo[2]
+ },{where: {id:v2.id}});
+ })
+ .then(()=>{
+ return packageInfo
+ });
}
+ return packageInfo;
})
- .then(() => {
-
+ .then((packageInfo) => {
+ var new_params = {
+ description: description || packageInfo.description,
+ };
+ if (_.isInteger(rollout)) {
+ new_params.rollout = rollout;
+ }
+ if (_.isBoolean(isMandatory)) {
+ new_params.is_mandatory = isMandatory ? constConfig.IS_MANDATORY_YES : constConfig.IS_MANDATORY_NO;
+ }
+ if (_.isBoolean(isDisabled)) {
+ new_params.is_disabled = isDisabled ? constConfig.IS_DISABLED_YES : constConfig.IS_DISABLED_NO;
+ }
+ return models.Packages.update(new_params,{where: {id: packageId}});
});
};
-proto.promotePackage = function (sourceDeploymentId, destDeploymentId, promoteUid) {
+proto.promotePackage = function (sourceDeploymentInfo, destDeploymentInfo, params) {
var self = this;
- return models.Deployments.findById(sourceDeploymentId)
- .then((sourceDeployment) => {
- var lastDeploymentVersionId = _.get(sourceDeployment, 'last_deployment_version_id', 0);
- if (_.lte(lastDeploymentVersionId, 0)) {
- throw new AppError.AppError('does not exist last_deployment_version_id.');
- }
- return models.DeploymentsVersions.findById(lastDeploymentVersionId)
- .then((deploymentsVersions) => {
- var packageId = _.get(deploymentsVersions, 'current_package_id', 0);
- if (_.lte(packageId, 0)) {
- throw new AppError.AppError('does not exist packages.');
+ var appVersion = _.get(params,'appVersion', null);
+ var label = _.get(params,'label', null);
+ return new Promise((resolve, reject) => {
+ if (label) {
+ return models.Packages.findOne({where: {deployment_id: sourceDeploymentInfo.id, label:label}})
+ .then((sourcePack)=>{
+ if (!sourcePack) {
+ throw new AppError.AppError('label does not exist.');
+ }
+ return models.DeploymentsVersions.findById(sourcePack.deployment_version_id)
+ .then((deploymentsVersions)=>{
+ if (!deploymentsVersions) {
+ throw new AppError.AppError('deploymentsVersions does not exist.');
+ }
+ resolve([sourcePack, deploymentsVersions]);
+ });
+ })
+ .catch((e) => {
+ reject(e);
+ });
+ } else {
+ var lastDeploymentVersionId = _.get(sourceDeploymentInfo, 'last_deployment_version_id', 0);
+ if (_.lte(lastDeploymentVersionId, 0)) {
+ throw new AppError.AppError(`does not exist last_deployment_version_id.`);
}
- return models.Packages.findById(packageId)
- .then((packages) => {
- if (!packages) {
- throw new AppError.AppError('does not exist packages.');
+ return models.DeploymentsVersions.findById(lastDeploymentVersionId)
+ .then((deploymentsVersions)=>{
+ var sourcePackId = _.get(deploymentsVersions, 'current_package_id', 0);
+ if (_.lte(sourcePackId, 0)) {
+ throw new AppError.AppError(`packageInfo not found.`);
}
- return self.createDeploymentsVersionIfNotExist(destDeploymentId, deploymentsVersions.app_version)
- .then((deploymentsVersions) => {
- return self.isMatchPackageHash(deploymentsVersions.get('current_package_id'), packages.package_hash);
- })
- .then((isExist) => {
- if (isExist){
- throw new AppError.AppError("The uploaded package is identical to the contents of the specified deployment's current release.");
+ return models.Packages.findById(sourcePackId)
+ .then((sourcePack) =>{
+ if (!sourcePack) {
+ throw new AppError.AppError(`packageInfo not found.`);
}
- })
- .then(() => [sourceDeployment, deploymentsVersions, packages]);
+ resolve([sourcePack, deploymentsVersions]);
+ });
+ })
+ .catch((e) => {
+ reject(e);
});
+ }
+ })
+ .spread((sourcePack, deploymentsVersions)=>{
+ var appFinalVersion = appVersion || deploymentsVersions.app_version;
+ log.debug('sourcePack',sourcePack);
+ log.debug('deploymentsVersions',deploymentsVersions);
+ log.debug('appFinalVersion', appFinalVersion);
+ return models.DeploymentsVersions.findOne({where: {
+ deployment_id:destDeploymentInfo.id,
+ app_version: appFinalVersion,
+ }})
+ .then((destDeploymentsVersions)=>{
+ if (!destDeploymentsVersions) {
+ return false;
+ }
+ return self.isMatchPackageHash(destDeploymentsVersions.get('current_package_id'), sourcePack.package_hash);
+ })
+ .then((isExist) => {
+ if (isExist){
+ throw new AppError.AppError("The uploaded package is identical to the contents of the specified deployment's current release.");
+ }
+ return [sourcePack, deploymentsVersions, appFinalVersion];
});
})
- .spread((sourceDeployment, deploymentsVersions, packages) => {
- var params = {
- releaseMethod: 'Promote',
- releaseUid: promoteUid,
- isMandatory: packages.is_mandatory == 1 ? true : false,
- size: packages.size,
- description: packages.description,
- originalLabel: packages.label,
- originalDeployment: sourceDeployment.name
+ .spread((sourcePack, deploymentsVersions, appFinalVersion) => {
+ var versionInfo = common.validatorVersion(appFinalVersion);
+ if (!versionInfo[0]) {
+ log.debug(`targetBinaryVersion ${appVersion} not support.`);
+ throw new AppError.AppError(`targetBinaryVersion ${appVersion} not support.`);
+ }
+ var create_params = {
+ releaseMethod: constConfig.RELEAS_EMETHOD_PROMOTE,
+ releaseUid: params.promoteUid || 0,
+ rollout: params.rollout || 100,
+ size: sourcePack.size,
+ description: params.description || sourcePack.description,
+ originalLabel: sourcePack.label,
+ originalDeployment: sourceDeploymentInfo.name,
+ min_version: versionInfo[1],
+ max_version: versionInfo[2],
};
- return self.createPackage(destDeploymentId, deploymentsVersions.app_version, packages.package_hash, packages.manifest_blob_url, packages.blob_url, params);
+ if (_.isBoolean(params.isMandatory)) {
+ create_params.isMandatory = params.isMandatory ? constConfig.IS_MANDATORY_YES : constConfig.IS_MANDATORY_NO;
+ } else {
+ create_params.isMandatory = sourcePack.is_mandatory
+ }
+ if (_.isBoolean(params.isDisabled)) {
+ create_params.isDisabled = params.isDisabled ? constConfig.IS_DISABLED_YES : constConfig.IS_DISABLED_NO;
+ } else {
+ create_params.isDisabled = sourcePack.is_disabled
+ }
+ return self.createPackage(
+ destDeploymentInfo.id,
+ appFinalVersion,
+ sourcePack.package_hash,
+ sourcePack.manifest_blob_url,
+ sourcePack.blob_url,
+ create_params
+ );
});
};
@@ -476,11 +633,15 @@ proto.rollbackPackage = function (deploymentVersionId, targetLabel, rollbackUid)
var params = {
releaseMethod: 'Rollback',
releaseUid: rollbackUid,
- isMandatory: rollbackPackage.is_mandatory == 1 ? true : false,
+ isMandatory: rollbackPackage.is_mandatory,
+ isDisabled: rollbackPackage.is_disabled,
+ rollout: rollbackPackage.rollout,
size: rollbackPackage.size,
description: rollbackPackage.description,
originalLabel: rollbackPackage.label,
- originalDeployment: ''
+ originalDeployment: '',
+ min_version: deploymentsVersions.min_version,
+ max_version: deploymentsVersions.max_version,
};
return self.createPackage(deploymentsVersions.deployment_id,
deploymentsVersions.app_version,
@@ -494,7 +655,11 @@ proto.rollbackPackage = function (deploymentVersionId, targetLabel, rollbackUid)
}
proto.getCanRollbackPackages = function (deploymentVersionId) {
+ var Sequelize = require('sequelize');
return models.Packages.findAll({
- where: {deployment_version_id: deploymentVersionId, release_method: {$in: ['Upload', 'Promote'] }}, order: [['id','desc']], limit: 2
+ where: {
+ deployment_version_id: deploymentVersionId,
+ release_method: {[Sequelize.Op.in]: [constConfig.RELEAS_EMETHOD_UPLOAD, constConfig.RELEAS_EMETHOD_PROMOTE] }
+ }, order: [['id','desc']], limit: 2
});
}
diff --git a/core/utils/common.js b/core/utils/common.js
index ca328c18..6d221535 100644
--- a/core/utils/common.js
+++ b/core/utils/common.js
@@ -5,18 +5,91 @@ var fsextra = require("fs-extra");
var extract = require('extract-zip')
var config = require('../config');
var _ = require('lodash');
+var validator = require('validator');
var qiniu = require("qiniu");
+var upyun = require('upyun');
var common = {};
var AppError = require('../app-error');
+var jschardet = require("jschardet");
var log4js = require('log4js');
+var path = require('path');
var log = log4js.getLogger("cps:utils:common");
module.exports = common;
+common.detectIsTextFile = function(filePath) {
+ var fd = fs.openSync(filePath, 'r');
+ var buffer = new Buffer(4096);
+ fs.readSync(fd, buffer, 0, 4096, 0);
+ fs.closeSync(fd);
+ var rs = jschardet.detect(buffer);
+ log.debug('detectIsTextFile:', filePath, rs);
+ if (rs.confidence == 1) {
+ return true;
+ }
+ return false;
+}
+
+common.parseVersion = function (versionNo) {
+ var version = '0';
+ var data = null;
+ if (data = versionNo.match(/^([0-9]{1,3}).([0-9]{1,5}).([0-9]{1,10})$/)) {
+ // "1.2.3"
+ version = data[1] + _.padStart(data[2], 5, '0') + _.padStart(data[3], 10, '0');
+ } else if (data = versionNo.match(/^([0-9]{1,3}).([0-9]{1,5})$/)) {
+ // "1.2"
+ version = data[1] + _.padStart(data[2], 5, '0') + _.padStart('0', 10, '0');
+ }
+ return version;
+};
+
+common.validatorVersion = function (versionNo) {
+ var flag = false;
+ var min = '0';
+ var max = '9999999999999999999';
+ var data = null;
+ if (versionNo == "*") {
+ // "*"
+ flag = true;
+ } else if (data = versionNo.match(/^([0-9]{1,3}).([0-9]{1,5}).([0-9]{1,10})$/)) {
+ // "1.2.3"
+ flag = true;
+ min = data[1] + _.padStart(data[2], 5, '0') + _.padStart(data[3], 10, '0');
+ max = data[1] + _.padStart(data[2], 5, '0') + _.padStart((parseInt(data[3])+1), 10, '0');
+ } else if (data = versionNo.match(/^([0-9]{1,3}).([0-9]{1,5})(\.\*){0,1}$/)) {
+ // "1.2" "1.2.*"
+ flag = true;
+ min = data[1] + _.padStart(data[2], 5, '0') + _.padStart('0', 10, '0');
+ max = data[1] + _.padStart((parseInt(data[2])+1), 5, '0') + _.padStart('0', 10, '0');
+ } else if (data = versionNo.match(/^\~([0-9]{1,3}).([0-9]{1,5}).([0-9]{1,10})$/)) {
+ //"~1.2.3"
+ flag = true;
+ min = data[1] + _.padStart(data[2], 5, '0') + _.padStart(data[3], 10, '0');
+ max = data[1] + _.padStart((parseInt(data[2])+1), 5, '0') + _.padStart('0', 10, '0');
+ } else if (data = versionNo.match(/^\^([0-9]{1,3}).([0-9]{1,5}).([0-9]{1,10})$/)) {
+ //"^1.2.3"
+ flag = true;
+ min = data[1] + _.padStart(data[2], 5, '0') + _.padStart(data[3], 10, '0');
+ max = _.toString((parseInt(data[1])+1)) + _.padStart(0, 5, '0') + _.padStart('0', 10, '0');
+ } else if (data = versionNo.match(/^([0-9]{1,3}).([0-9]{1,5}).([0-9]{1,10})\s?-\s?([0-9]{1,3}).([0-9]{1,5}).([0-9]{1,10})$/)) {
+ // "1.2.3 - 1.2.7"
+ flag = true;
+ min = data[1] + _.padStart(data[2], 5, '0') + _.padStart(data[3], 10, '0');
+ max = data[4] + _.padStart(data[5], 5, '0') + _.padStart((parseInt(data[6])+1), 10, '0');
+ } else if (data = versionNo.match(/^>=([0-9]{1,3}).([0-9]{1,5}).([0-9]{1,10})\s?<([0-9]{1,3}).([0-9]{1,5}).([0-9]{1,10})$/)) {
+ // ">=1.2.3 <1.2.7"
+ flag = true;
+ min = data[1] + _.padStart(data[2], 5, '0') + _.padStart(data[3], 10, '0');
+ max = data[4] + _.padStart(data[5], 5, '0') + _.padStart(data[6], 10, '0');
+ }
+ return [flag, min, max];
+};
+
common.createFileFromRequest = function (url, filePath) {
return new Promise((resolve, reject) => {
fs.exists(filePath, function (exists) {
if (!exists) {
var request = require('request');
+ log.debug(`createFileFromRequest url:${url}`)
request(url).on('error', function (error) {
reject(error);
})
@@ -39,11 +112,29 @@ common.createFileFromRequest = function (url, filePath) {
}
});
});
-}
+};
+
+common.copySync = function (sourceDst, targertDst) {
+ return fsextra.copySync(sourceDst, targertDst, {overwrite: true});
+};
+
+common.copy = function (sourceDst, targertDst) {
+ return new Promise((resolve, reject) => {
+ fsextra.copy(sourceDst, targertDst, {overwrite: true}, function (err) {
+ if (err) {
+ log.error(err);
+ reject(err);
+ } else {
+ log.debug(`copy success sourceDst:${sourceDst} targertDst:${targertDst}`);
+ resolve();
+ }
+ });
+ });
+};
common.move = function (sourceDst, targertDst) {
return new Promise((resolve, reject) => {
- fsextra.move(sourceDst, targertDst, {clobber: true, limit: 16}, function (err) {
+ fsextra.move(sourceDst, targertDst, {overwrite: true}, function (err) {
if (err) {
log.error(err);
reject(err);
@@ -117,20 +208,30 @@ common.unzipFile = function (zipFile, outputPath) {
});
};
-common.uptoken = function (bucket, key) {
- var putPolicy = new qiniu.rs.PutPolicy(bucket+":"+key);
- return putPolicy.token();
+common.getUploadTokenQiniu = function (mac, bucket, key) {
+ var options = {
+ scope: bucket + ":" + key
+ }
+ var putPolicy = new qiniu.rs.PutPolicy(options);
+ return putPolicy.uploadToken(mac);
};
common.uploadFileToStorage = function (key, filePath) {
- if (_.get(config, 'common.storageType') === 'local') {
+ var storageType = _.get(config, 'common.storageType');
+ if ( storageType === 'local') {
return common.uploadFileToLocal(key, filePath);
- } else if (_.get(config, 'common.storageType') === 's3') {
+ } else if (storageType === 's3') {
return common.uploadFileToS3(key, filePath);
- } else if (_.get(config, 'common.storageType') === 'oss') {
+ } else if (storageType === 'oss') {
return common.uploadFileToOSS(key, filePath);
+ } else if (storageType === 'qiniu') {
+ return common.uploadFileToQiniu(key, filePath);
+ } else if (storageType === 'upyun') {
+ return common.uploadFileToUpyun(key, filePath);
+ } else if (storageType === 'tencentcloud') {
+ return common.uploadFileToTencentCloud(key, filePath);
}
- return common.uploadFileToQiniu(key, filePath);
+ throw new AppError.AppError(`${storageType} storageType does not support.`);
};
common.uploadFileToLocal = function (key, filePath) {
@@ -139,6 +240,10 @@ common.uploadFileToLocal = function (key, filePath) {
if (!storageDir) {
throw new AppError.AppError('please set config local storageDir');
}
+ if (key.length < 3) {
+ log.error(`generate key is too short, key value:${key}`);
+ throw new AppError.AppError('generate key is too short.');
+ }
try {
log.debug(`uploadFileToLocal check directory ${storageDir} fs.R_OK`);
fs.accessSync(storageDir, fs.W_OK);
@@ -147,7 +252,10 @@ common.uploadFileToLocal = function (key, filePath) {
log.error(e);
throw new AppError.AppError(e.message);
}
- if (fs.existsSync(`${storageDir}/${key}`)) {
+ var subDir = key.substr(0, 2).toLowerCase();
+ var finalDir = path.join(storageDir, subDir);
+ var fileName = path.join(finalDir, key);
+ if (fs.existsSync(fileName)) {
return resolve(key);
}
var stats = fs.statSync(storageDir);
@@ -156,6 +264,10 @@ common.uploadFileToLocal = function (key, filePath) {
log.error(e);
throw e;
}
+ if (!fs.existsSync(`${finalDir}`)) {
+ fs.mkdirSync(`${finalDir}`);
+ log.debug(`uploadFileToLocal mkdir:${finalDir}`);
+ }
try {
fs.accessSync(filePath, fs.R_OK);
} catch (e) {
@@ -165,10 +277,10 @@ common.uploadFileToLocal = function (key, filePath) {
stats = fs.statSync(filePath);
if (!stats.isFile()) {
var e = new AppError.AppError(`${filePath} must be file`);
- log.debug(e);
+ log.error(e);
throw e;
}
- fsextra.copy(filePath, `${storageDir}/${key}`,(err) => {
+ fsextra.copy(filePath, fileName,(err) => {
if (err) {
log.error(new AppError.AppError(err.message));
return reject(new AppError.AppError(err.message));
@@ -179,44 +291,61 @@ common.uploadFileToLocal = function (key, filePath) {
});
};
-common.getDownloadUrl = function () {
- if (_.get(config, 'common.storageType') === 'local') {
- return _.get(config, 'local.downloadUrl');
- } else if (_.get(config, 'common.storageType') === 's3') {
- return _.get(config, 's3.downloadUrl');
- } else if (_.get(config, 'common.storageType') === 'oss') {
- return _.get(config, 'oss.downloadUrl');
- }
- return _.get(config, 'qiniu.downloadUrl');
-}
-
common.getBlobDownloadUrl = function (blobUrl) {
- return `${common.getDownloadUrl()}/${blobUrl}`
+ var fileName = blobUrl;
+ var storageType = _.get(config, 'common.storageType');
+ var downloadUrl = _.get(config, `${storageType}.downloadUrl`);
+ if ( storageType === 'local') {
+ fileName = blobUrl.substr(0, 2).toLowerCase() + '/' + blobUrl;
+ }
+ if (!validator.isURL(downloadUrl)) {
+ var e = new AppError.AppError(`Please config ${storageType}.downloadUrl in config.js`);
+ log.error(e);
+ throw e;
+ }
+ return `${downloadUrl}/${fileName}`
};
+
common.uploadFileToQiniu = function (key, filePath) {
return new Promise((resolve, reject) => {
- qiniu.conf.ACCESS_KEY = _.get(config, "qiniu.accessKey");
- qiniu.conf.SECRET_KEY = _.get(config, "qiniu.secretKey");
- var bucket = _.get(config, "qiniu.bucketName", "jukang");
- var client = new qiniu.rs.Client();
- client.stat(bucket, key, (err, ret) => {
- if (!err) {
- resolve(ret.hash);
+ var accessKey = _.get(config, "qiniu.accessKey");
+ var secretKey = _.get(config, "qiniu.secretKey");
+ var bucket = _.get(config, "qiniu.bucketName", "");
+ var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
+ var conf = new qiniu.conf.Config();
+ var bucketManager = new qiniu.rs.BucketManager(mac, conf);
+ bucketManager.stat(bucket, key, (respErr, respBody, respInfo) => {
+ if (respErr) {
+ log.debug('uploadFileToQiniu file stat:', respErr);
+ return reject(new AppError.AppError(respErr.message));
+ }
+ log.debug('uploadFileToQiniu file stat respBody:', respBody);
+ log.debug('uploadFileToQiniu file stat respInfo:', respInfo);
+ if (respInfo.statusCode == 200) {
+ resolve(respBody.hash);
} else {
try {
- var uptoken = common.uptoken(bucket, key);
+ var uploadToken = common.getUploadTokenQiniu(mac, bucket, key);
} catch (e) {
- return reject(e);
+ return reject(new AppError.AppError(e.message));
}
- var extra = new qiniu.io.PutExtra();
- qiniu.io.putFile(uptoken, key, filePath, extra, (err, ret) => {
- if(!err) {
- // 上传成功, 处理返回值
- resolve(ret.hash);
- } else {
+ var formUploader = new qiniu.form_up.FormUploader(conf);
+ var putExtra = new qiniu.form_up.PutExtra();
+ formUploader.putFile(uploadToken, key, filePath, putExtra, (respErr, respBody, respInfo) => {
+ if(respErr) {
+ log.error('uploadFileToQiniu putFile:', respErr);
// 上传失败, 处理返回代码
- reject(new AppError.AppError(JSON.stringify(err)));
+ return reject(new AppError.AppError(JSON.stringify(respErr)));
+ } else {
+ log.debug('uploadFileToQiniu putFile respBody:', respBody);
+ log.debug('uploadFileToQiniu putFile respInfo:', respInfo);
+ // 上传成功, 处理返回值
+ if (respInfo.statusCode == 200) {
+ return resolve(respBody.hash);
+ } else {
+ return reject(new AppError.AppError(respBody.error));
+ }
}
});
}
@@ -224,6 +353,43 @@ common.uploadFileToQiniu = function (key, filePath) {
});
};
+common.uploadFileToUpyun = function (key, filePath) {
+ var serviceName = _.get(config, "upyun.serviceName");
+ var operatorName = _.get(config, "upyun.operatorName");
+ var operatorPass = _.get(config, "upyun.operatorPass", "");
+ var storageDir = _.get(config, "upyun.storageDir", "");
+ var service = new upyun.Service(serviceName, operatorName, operatorPass);
+ var client = new upyun.Client(service);
+ return (
+ new Promise((resolve, reject) => {
+ client.makeDir(storageDir).then(result => {
+ if(!storageDir) {
+ reject(new AppError.AppError('Please config the upyun remoteDir!'));
+ return;
+ }
+ let remotePath = storageDir + '/' + key;
+ log.debug('uploadFileToUpyun remotePath:', remotePath);
+ log.debug('uploadFileToUpyun mkDir result:', result);
+ client.putFile(remotePath, fs.createReadStream(filePath)).then(data => {
+ log.debug('uploadFileToUpyun putFile response:', data);
+ if(data) {
+ resolve(key)
+ } else {
+ log.debug('uploadFileToUpyun putFile failed!', data);
+ reject(new AppError.AppError('Upload file to upyun failed!'));
+ }
+ }).catch(e1 => {
+ log.debug('uploadFileToUpyun putFile exception e1:', e1);
+ reject(new AppError.AppError(JSON.stringify(e1)));
+ })
+ }).catch(e => {
+ log.debug('uploadFileToUpyun putFile exception e:', e);
+ reject(new AppError.AppError(JSON.stringify(e)));
+ });
+ })
+ );
+};
+
common.uploadFileToS3 = function (key, filePath) {
var AWS = require('aws-sdk');
return (
@@ -272,16 +438,41 @@ common.uploadFileToOSS = function (key, filePath) {
return new Promise((resolve, reject) => {
upload.on('error', (error) => {
+ log.debug("uploadFileToOSS", error);
reject(error);
});
upload.on('uploaded', (details) => {
+ log.debug("uploadFileToOSS", details);
resolve(details.ETag);
});
fs.createReadStream(filePath).pipe(upload);
});
};
+common.uploadFileToTencentCloud = function (key, filePath) {
+ return new Promise((resolve, reject) => {
+ var COS = require('cos-nodejs-sdk-v5');
+ var cosIn = new COS({
+ SecretId: _.get(config, 'tencentcloud.accessKeyId'),
+ SecretKey: _.get(config, 'tencentcloud.secretAccessKey')
+ });
+ cosIn.sliceUploadFile({
+ Bucket: _.get(config, 'tencentcloud.bucketName'),
+ Region: _.get(config, 'tencentcloud.region'),
+ Key: key,
+ FilePath: filePath
+ }, function (err, data) {
+ log.debug("uploadFileToTencentCloud", err, data);
+ if (err) {
+ reject(new AppError.AppError(JSON.stringify(err)));
+ }else {
+ resolve(data.Key);
+ }
+ });
+ });
+}
+
common.diffCollectionsSync = function (collection1, collection2) {
var diffFiles = [];
var collection1Only = [];
diff --git a/core/utils/security.js b/core/utils/security.js
index 401fd040..a4054acd 100644
--- a/core/utils/security.js
+++ b/core/utils/security.js
@@ -61,13 +61,15 @@ security.stringSha256Sync = function (contents) {
security.packageHashSync = function (jsonData) {
var sortedArr = security.sortJsonToArr(jsonData);
- var manifestData = _.map(sortedArr, (v) => {
+ var manifestData = _.filter(sortedArr, (v) => {
+ return !security.isPackageHashIgnored(v.path);
+ }).map((v) => {
return v.path + ':' + v.hash;
});
log.debug('packageHashSync manifestData:', manifestData);
var manifestString = JSON.stringify(manifestData.sort());
manifestString = _.replace(manifestString, /\\\//g, '/');
- log.debug('packageHashSync manifestString:', manifestData);
+ log.debug('packageHashSync manifestString:', manifestString);
return security.stringSha256Sync(manifestString);
}
@@ -112,28 +114,29 @@ security.sha256AllFiles = function (files) {
security.uploadPackageType = function (directoryPath) {
return new Promise((resolve, reject) => {
- var recursiveFs = require("recursive-fs");
+ var recursive = require("recursive-readdir");
var path = require('path');
var slash = require("slash");
- recursiveFs.readdirr(directoryPath, (err, directories, files) => {
+ recursive(directoryPath, (err, files) => {
if (err) {
log.error(new AppError.AppError(err.message));
- reject(err);
+ reject(new AppError.AppError(err.message));
} else {
if (files.length == 0) {
log.debug(`uploadPackageType empty files`);
reject(new AppError.AppError("empty files"));
} else {
+ var constName = require('../const');
const AREGEX=/android\.bundle/
const AREGEX_IOS=/main\.jsbundle/
var packageType = 0;
_.forIn(files, function (value) {
if (AREGEX.test(value)) {
- packageType = 1;
+ packageType = constName.ANDROID;
return false;
}
if (AREGEX_IOS.test(value)) {
- packageType = 2;
+ packageType = constName.IOS;
return false;
}
});
@@ -145,16 +148,51 @@ security.uploadPackageType = function (directoryPath) {
});
}
+// some files are ignored in calc hash in client sdk
+// https://github.com/Microsoft/react-native-code-push/pull/974/files#diff-21b650f88429c071b217d46243875987R15
+security.isHashIgnored = function (relativePath) {
+ if (!relativePath) {
+ return true;
+ }
+
+ const IgnoreMacOSX = '__MACOSX/';
+ const IgnoreDSStore = '.DS_Store';
+
+ return relativePath.startsWith(IgnoreMacOSX)
+ || relativePath === IgnoreDSStore
+ || relativePath.endsWith(IgnoreDSStore);
+}
+
+security.isPackageHashIgnored = function (relativePath) {
+ if (!relativePath) {
+ return true;
+ }
+
+ // .codepushrelease contains code sign JWT
+ // it should be ignored in package hash but need to be included in package manifest
+ const IgnoreCodePushMetadata = '.codepushrelease';
+ return relativePath === IgnoreCodePushMetadata
+ || relativePath.endsWith(IgnoreCodePushMetadata)
+ || security.isHashIgnored(relativePath);
+}
+
+
security.calcAllFileSha256 = function (directoryPath) {
return new Promise((resolve, reject) => {
- var recursiveFs = require("recursive-fs");
+ var recursive = require("recursive-readdir");
var path = require('path');
var slash = require("slash");
- recursiveFs.readdirr(directoryPath, (error, directories, files) => {
+ recursive(directoryPath, (error, files) => {
if (error) {
log.error(error);
- reject(error);
+ reject(new AppError.AppError(error.message));
} else {
+ // filter files that should be ignored
+ files = files.filter((file) => {
+ var relative = path.relative(directoryPath, file);
+ return !security.isHashIgnored(relative);
+ });
+
if (files.length == 0) {
log.debug(`calcAllFileSha256 empty files in directoryPath:`, directoryPath);
reject(new AppError.AppError("empty files"));
@@ -164,6 +202,10 @@ security.calcAllFileSha256 = function (directoryPath) {
var data = {};
_.forIn(results, (value, key) => {
var relativePath = path.relative(directoryPath, key);
+ var matchresult = relativePath.match(/(\/|\\).*/);
+ if (matchresult) {
+ relativePath = path.join('CodePush', matchresult[0]);
+ }
relativePath = slash(relativePath);
data[relativePath] = value;
});
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 00000000..d1fb6da1
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,137 @@
+# docker 部署 code-push-server
+
+>该文档用于描述docker部署code-push-server,实例包含三个部分
+
+- code-push-server部分
+ - 更新包默认采用`local`存储(即存储在本地机器上)。使用docker volume存储方式,容器销毁不会导致数据丢失,除非人为删除volume。
+ - 内部使用pm2 cluster模式管理进程,默认开启进程数为cpu数,可以根据自己机器配置设置docker-compose.yml文件中deploy参数。
+ - docker-compose.yml只提供了应用的一部分参数设置,如需要设置其他配置,可以修改文件config.js。
+- mysql部分
+ - 数据使用docker volume存储方式,容器销毁不会导致数据丢失,除非人为删除volume。
+ - 应用请勿使用root用户,为了安全可以创建权限相对较小的权限供code-push-server使用,只需要给予`select,update,insert`权限即可。初始化数据库需要使用root或有建表权限用户
+- redis部分
+ - `tryLoginTimes` 登录错误次数限制
+ - `updateCheckCache` 提升应用性能
+ - `rolloutClientUniqueIdCache` 灰度发布
+
+## 安装docker
+
+参考docker官方安装教程
+
+- [>>mac点这里](https://docs.docker.com/docker-for-mac/install/)
+- [>>windows点这里](https://docs.docker.com/docker-for-windows/install/)
+- [>>linux点这里](https://docs.docker.com/install/linux/docker-ce/ubuntu/)
+
+
+`$ docker info` 能成功输出相关信息,则安装成功,才能继续下面步骤
+
+## 启动swarm
+
+```shell
+$ sudo docker swarm init
+```
+
+
+## 获取代码
+
+```shell
+$ git clone https://github.com/lisong/code-push-server.git
+$ cd code-push-server/docker
+```
+
+## 修改配置文件
+
+```shell
+$ vim docker-compose.yml
+```
+
+*将`DOWNLOAD_URL`中`YOU_MACHINE_IP`替换成本机外网ip或者域名*
+
+*将`MYSQL_HOST`中`YOU_MACHINE_IP`替换成本机内网ip*
+
+*将`REDIS_HOST`中`YOU_MACHINE_IP`替换成本机内网ip*
+
+## jwt.tokenSecret修改
+
+> code-push-server 验证登录验证方式使用的json web token加密方式,该对称加密算法是公开的,所以修改config.js中tokenSecret值很重要。
+
+*非常重要!非常重要! 非常重要!*
+
+> 可以打开连接`https://www.grc.com/passwords.htm`获取 `63 random alpha-numeric characters`类型的随机生成数作为密钥
+
+## 部署
+
+```shell
+$ sudo docker stack deploy -c docker-compose.yml code-push-server
+```
+
+> 如果网速不佳,需要漫长而耐心的等待。。。去和妹子聊会天吧^_^
+
+
+## 查看进展
+
+```shell
+$ sudo docker service ls
+$ sudo docker service ps code-push-server_db
+$ sudo docker service ps code-push-server_redis
+$ sudo docker service ps code-push-server_server
+```
+
+> 确认`CURRENT STATE` 为 `Running about ...`, 则已经部署完成
+
+## 访问接口简单验证
+
+`$ curl -I http://YOUR_CODE_PUSH_SERVER_IP:3000/`
+
+返回`200 OK`
+
+```http
+HTTP/1.1 200 OK
+X-DNS-Prefetch-Control: off
+X-Frame-Options: SAMEORIGIN
+Strict-Transport-Security: max-age=15552000; includeSubDomains
+X-Download-Options: noopen
+X-Content-Type-Options: nosniff
+X-XSS-Protection: 1; mode=block
+Content-Type: text/html; charset=utf-8
+Content-Length: 592
+ETag: W/"250-IiCMcM1ZUFSswSYCU0KeFYFEMO8"
+Date: Sat, 25 Aug 2018 15:45:46 GMT
+Connection: keep-alive
+```
+
+## 浏览器登录
+
+> 默认用户名:admin 密码:123456 记得要修改默认密码哦
+> 如果登录连续输错密码超过一定次数,会限定无法再登录. 需要清空redis缓存
+
+```shell
+$ redis-cli -p6388 # 进入redis
+> flushall
+> quit
+```
+
+
+## 查看服务日志
+
+```shell
+$ sudo docker service logs code-push-server_server
+$ sudo docker service logs code-push-server_db
+$ sudo docker service logs code-push-server_redis
+```
+
+## 查看存储 `docker volume ls`
+
+DRIVER | VOLUME NAME | 描述
+------ | ----- | -------
+local | code-push-server_data-mysql | 数据库存储数据目录
+local | code-push-server_data-storage | 存储打包文件目录
+local | code-push-server_data-tmp | 用于计算更新包差异文件临时目录
+local | code-push-server_data-redis | redis落地数据
+
+## 销毁退出应用
+
+```bash
+$ sudo docker stack rm code-push-server
+$ sudo docker swarm leave --force
+```
diff --git a/docker/code-push-server/Dockerfile b/docker/code-push-server/Dockerfile
new file mode 100644
index 00000000..004406ba
--- /dev/null
+++ b/docker/code-push-server/Dockerfile
@@ -0,0 +1,10 @@
+FROM node:8.11.4-alpine
+
+RUN npm config set registry https://registry.npm.taobao.org/ \
+&& npm i -g code-push-server@0.5.2 pm2@latest --no-optional
+
+COPY ./process.json /process.json
+
+EXPOSE 3000
+
+CMD ["pm2-docker", "start", "/process.json"]
diff --git a/docker/code-push-server/process.json b/docker/code-push-server/process.json
new file mode 100644
index 00000000..493010d3
--- /dev/null
+++ b/docker/code-push-server/process.json
@@ -0,0 +1,11 @@
+{
+ "apps" : [
+ {
+ "name" : "code-push-server",
+ "max_memory_restart" : "500M",
+ "script" : "code-push-server",
+ "instances" : "max", //开启实例数量,max为cpu核数
+ "exec_mode" : "cluster", //集群模式,最大提升网站并发
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docker/config.js b/docker/config.js
new file mode 100644
index 00000000..f78fc3b6
--- /dev/null
+++ b/docker/config.js
@@ -0,0 +1,93 @@
+var config = {};
+config.development = {
+ // Config for database, only support mysql.
+ db: {
+ username: process.env.MYSQL_USERNAME,
+ password: process.env.MYSQL_PASSWORD,
+ database: process.env.MYSQL_DATABASE,
+ host: process.env.MYSQL_HOST,
+ port: process.env.MYSQL_PORT || 3306,
+ dialect: "mysql",
+ logging: false,
+ operatorsAliases: false,
+ },
+ // Config for local storage when storageType value is "local".
+ local: {
+ // Binary files storage dir, Do not use tmpdir and it's public download dir.
+ storageDir: process.env.STORAGE_DIR,
+ // Binary files download host address which Code Push Server listen to. the files storage in storageDir.
+ downloadUrl: process.env.DOWNLOAD_URL,
+ // public static download spacename.
+ public: '/download'
+ },
+ jwt: {
+ // Recommended: 63 random alpha-numeric characters
+ // Generate using: https://www.grc.com/passwords.htm
+ tokenSecret: 'INSERT_RANDOM_TOKEN_KEY'
+ },
+ common: {
+ /*
+ * tryLoginTimes is control login error times to avoid force attack.
+ * if value is 0, no limit for login auth, it may not safe for account. when it's a number, it means you can
+ * try that times today. but it need config redis server.
+ */
+ tryLoginTimes: 4,
+ // CodePush Web(https://github.com/lisong/code-push-web) login address.
+ //codePushWebUrl: "http://127.0.0.1:3001/login",
+ // create patch updates's number. default value is 3
+ diffNums: 3,
+ // data dir for caclulate diff files. it's optimization.
+ dataDir: process.env.DATA_DIR,
+ // storageType which is your binary package files store. options value is ("local" | "qiniu" | "s3")
+ storageType: "local",
+ // options value is (true | false), when it's true, it will cache updateCheck results in redis.
+ updateCheckCache: false,
+ // options value is (true | false), when it's true, it will cache rollout results in redis
+ rolloutClientUniqueIdCache: false,
+ },
+ // Config for smtp email,register module need validate user email project source https://github.com/nodemailer/nodemailer
+ smtpConfig:{
+ host: "smtp.aliyun.com",
+ port: 465,
+ secure: true,
+ auth: {
+ user: "",
+ pass: ""
+ }
+ },
+ // Config for redis (register module, tryLoginTimes module)
+ redis: {
+ default: {
+ host: process.env.REDIS_HOST,
+ port: process.env.REDIS_PORT || 6379,
+ retry_strategy: function (options) {
+ if (options.error.code === 'ECONNREFUSED') {
+ // End reconnecting on a specific error and flush all commands with a individual error
+ return new Error('The server refused the connection');
+ }
+ if (options.total_retry_time > 1000 * 60 * 60) {
+ // End reconnecting after a specific timeout and flush all commands with a individual error
+ return new Error('Retry time exhausted');
+ }
+ if (options.times_connected > 10) {
+ // End reconnecting with built in error
+ return undefined;
+ }
+ // reconnect after
+ return Math.max(options.attempt * 100, 3000);
+ }
+ }
+ }
+}
+
+config.development.log4js = {
+ appenders: {console: { type: 'console'}},
+ categories : {
+ "default": { appenders: ['console'], level:'error'},
+ "startup": { appenders: ['console'], level:'info'},
+ "http": { appenders: ['console'], level:'info'}
+ }
+}
+
+config.production = Object.assign({}, config.development);
+module.exports = config;
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
new file mode 100644
index 00000000..2b625f8f
--- /dev/null
+++ b/docker/docker-compose.yml
@@ -0,0 +1,63 @@
+version: "3.7"
+services:
+ server:
+ image: tablee/code-push-server:v0.5.2
+ volumes:
+ - data-storage:/data/storage
+ - data-tmp:/data/tmp
+ - ./config.js:/config.js
+ environment:
+ DOWNLOAD_URL: "http://YOU_MACHINE_IP:3000/download"
+ MYSQL_HOST: "YOU_MACHINE_IP"
+ MYSQL_PORT: "3308"
+ MYSQL_USERNAME: "codepush"
+ MYSQL_PASSWORD: "123456"
+ MYSQL_DATABASE: "codepush"
+ STORAGE_DIR: "/data/storage"
+ DATA_DIR: "/data/tmp"
+ NODE_ENV: "production"
+ CONFIG_FILE: "/config.js"
+ REDIS_HOST: "YOU_MACHINE_IP"
+ REDIS_PORT: "6388"
+ deploy:
+ resources:
+ limits:
+ cpus: "2"
+ memory: 1000M
+ restart_policy:
+ condition: on-failure
+ ports:
+ - "3000:3000"
+ networks:
+ - servernet
+ depends_on:
+ - db
+ - redis
+ db:
+ image: mysql:5.7.23
+ volumes:
+ - data-mysql:/var/lib/mysql
+ - ./sql/codepush-all.sql:/docker-entrypoint-initdb.d/codepush-all.sql
+ ports:
+ - "3308:3306"
+ environment:
+ MYSQL_ALLOW_EMPTY_PASSWORD: "On"
+ networks:
+ - dbnet
+ redis:
+ image: redis:4.0.11-alpine
+ volumes:
+ - data-redis:/data
+ ports:
+ - "6388:6379"
+ networks:
+ - redisnet
+networks:
+ servernet:
+ dbnet:
+ redisnet:
+volumes:
+ data-storage:
+ data-tmp:
+ data-mysql:
+ data-redis:
diff --git a/sql/codepush-v0.0.1.sql b/docker/sql/codepush-all.sql
similarity index 64%
rename from sql/codepush-v0.0.1.sql
rename to docker/sql/codepush-all.sql
index ee7ed007..7bf4047d 100644
--- a/sql/codepush-v0.0.1.sql
+++ b/docker/sql/codepush-all.sql
@@ -1,17 +1,25 @@
-DROP TABLE IF EXISTS `apps`;
-CREATE TABLE `apps` (
+CREATE DATABASE IF NOT EXISTS `codepush`;
+
+GRANT SELECT,UPDATE,INSERT ON `codepush`.* TO 'codepush'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;
+
+flush privileges;
+
+use `codepush`;
+CREATE TABLE IF NOT EXISTS `apps` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL DEFAULT '',
`uid` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `os` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `platform` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `is_use_diff_text` tinyint(3) unsigned NOT NULL DEFAULT '0',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_name` (`name`(12))
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `collaborators`;
-CREATE TABLE `collaborators` (
+CREATE TABLE IF NOT EXISTS `collaborators` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`appid` int(10) unsigned NOT NULL DEFAULT '0',
`uid` bigint(20) unsigned NOT NULL DEFAULT '0',
@@ -25,8 +33,7 @@ CREATE TABLE `collaborators` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `deployments`;
-CREATE TABLE `deployments` (
+CREATE TABLE IF NOT EXISTS `deployments` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`appid` int(10) unsigned NOT NULL DEFAULT '0',
`name` varchar(20) NOT NULL DEFAULT '',
@@ -42,8 +49,7 @@ CREATE TABLE `deployments` (
KEY `idx_deploymentkey` (`deployment_key`(40))
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `deployments_history`;
-CREATE TABLE `deployments_history` (
+CREATE TABLE IF NOT EXISTS `deployments_history` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`deployment_id` int(11) unsigned NOT NULL DEFAULT '0',
`package_id` int(10) unsigned NOT NULL DEFAULT '0',
@@ -54,20 +60,23 @@ CREATE TABLE `deployments_history` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `deployments_versions`;
-CREATE TABLE `deployments_versions` (
+CREATE TABLE IF NOT EXISTS `deployments_versions` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`deployment_id` int(11) unsigned NOT NULL DEFAULT '0',
- `app_version` varchar(14) NOT NULL DEFAULT '',
+ `app_version` varchar(100) NOT NULL DEFAULT '',
`current_package_id` int(10) unsigned NOT NULL DEFAULT '0',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_at` timestamp NULL DEFAULT NULL,
+ `deleted_at` timestamp NULL DEFAULT NULL,
+ `min_version` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `max_version` bigint(20) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
- UNIQUE KEY `idx_did_appversion` (`deployment_id`,`app_version`)
+ KEY `idx_did_minversion` (`deployment_id`,`min_version`),
+ KEY `idx_did_maxversion` (`deployment_id`,`max_version`),
+ KEY `idx_did_appversion` (`deployment_id`,`app_version`(30))
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `packages`;
-CREATE TABLE `packages` (
+CREATE TABLE IF NOT EXISTS `packages` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`deployment_version_id` int(10) unsigned NOT NULL DEFAULT '0',
`deployment_id` int(10) unsigned NOT NULL DEFAULT '0',
@@ -82,14 +91,17 @@ CREATE TABLE `packages` (
`original_deployment` varchar(20) NOT NULL DEFAULT '',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_at` timestamp NULL DEFAULT NULL,
- `released_by` bigint(20) unsigned NOT NULL,
+ `released_by` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `is_mandatory` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `is_disabled` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rollout` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_deploymentid_label` (`deployment_id`,`label`(8)),
KEY `idx_versions_id` (`deployment_version_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `packages_diff`;
-CREATE TABLE `packages_diff` (
+CREATE TABLE IF NOT EXISTS `packages_diff` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`package_id` int(11) unsigned NOT NULL DEFAULT '0',
`diff_against_package_hash` varchar(64) NOT NULL DEFAULT '',
@@ -97,12 +109,12 @@ CREATE TABLE `packages_diff` (
`diff_size` int(11) unsigned NOT NULL DEFAULT '0',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_at` timestamp NULL DEFAULT NULL,
+ `deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_packageid_hash` (`package_id`,`diff_against_package_hash`(40))
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `packages_metrics`;
-CREATE TABLE `packages_metrics` (
+CREATE TABLE IF NOT EXISTS `packages_metrics` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`package_id` int(10) unsigned NOT NULL DEFAULT '0',
`active` int(10) unsigned NOT NULL DEFAULT '0',
@@ -111,12 +123,12 @@ CREATE TABLE `packages_metrics` (
`installed` int(10) unsigned NOT NULL DEFAULT '0',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_at` timestamp NULL DEFAULT NULL,
+ `deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
- UNIQUE KEY `udx_packageid` (`package_id`)
+ KEY `idx_packageid` (`package_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `user_tokens`;
-CREATE TABLE `user_tokens` (
+CREATE TABLE IF NOT EXISTS `user_tokens` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`uid` bigint(20) unsigned NOT NULL DEFAULT '0',
`name` varchar(50) NOT NULL DEFAULT '',
@@ -132,8 +144,7 @@ CREATE TABLE `user_tokens` (
KEY `idx_tokens` (`tokens`) KEY_BLOCK_SIZE=16
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `users`;
-CREATE TABLE `users` (
+CREATE TABLE IF NOT EXISTS `users` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL DEFAULT '',
`password` varchar(255) NOT NULL DEFAULT '',
@@ -150,4 +161,38 @@ CREATE TABLE `users` (
INSERT INTO `users` (`id`, `username`, `password`, `email`, `identical`, `ack_code`, `updated_at`, `created_at`)
VALUES
- (1,'admin','$2a$12$mvUY9kTqW4kSoGuZFDW0sOSgKmNY8SPHVyVrSckBTLtXKf6vKX3W.','lisong2010@gmail.com','4ksvOXqog','oZmGE','2016-11-14 10:46:55','2016-02-29 21:24:49');
+ (1,'admin','$2a$12$mvUY9kTqW4kSoGuZFDW0sOSgKmNY8SPHVyVrSckBTLtXKf6vKX3W.','lisong2010@gmail.com','4ksvOXqog','oZmGE','2016-11-14 10:46:55','2016-02-29 21:24:49');
+
+
+CREATE TABLE IF NOT EXISTS `versions` (
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+ `type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '1.DBversion',
+ `version` varchar(10) NOT NULL DEFAULT '',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `udx_type` (`type`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+LOCK TABLES `versions` WRITE;
+INSERT INTO `versions` (`id`, `type`, `version`)
+VALUES
+ (1,1,'0.5.0');
+UNLOCK TABLES;
+
+CREATE TABLE IF NOT EXISTS `log_report_deploy` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `status` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `package_id` int(10) unsigned NOT NULL DEFAULT '0',
+ `client_unique_id` varchar(100) NOT NULL DEFAULT '',
+ `previous_label` varchar(20) NOT NULL DEFAULT '',
+ `previous_deployment_key` varchar(64) NOT NULL DEFAULT '',
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `log_report_download` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `package_id` int(10) unsigned NOT NULL DEFAULT '0',
+ `client_unique_id` varchar(100) NOT NULL DEFAULT '',
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 00000000..9419e94d
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,238 @@
+
+## INSTALL NODE AND NPM
+
+[see](https://nodejs.org/en/download/)
+
+> (chosen latest LTS version)
+
+## INSTALL PM2
+
+```bash
+$ sudo npm i -g pm2
+```
+
+## INSTALL MYSQL
+
+- [Linux](https://dev.mysql.com/doc/refman/8.0/en/linux-installation.html)
+- [macOS](https://dev.mysql.com/doc/refman/8.0/en/osx-installation.html)
+- [Microsoft Windows](https://dev.mysql.com/doc/refman/8.0/en/windows-installation.html)
+- [Others](https://dev.mysql.com/doc/refman/8.0/en/installing.html)
+
+> notice. mysql8.x default auth caching_sha2_pasword not support in node-mysql2 see [issue](https://github.com/mysqljs/mysql/pull/1962)
+
+
+
+## GET code-push-server FROM NPM
+
+```shell
+$ npm install code-push-server@latest -g
+```
+
+
+## GET code-push-server FROM SOURCE CODE
+
+```shell
+$ git clone https://github.com/lisong/code-push-server.git
+$ cd code-push-server
+$ npm install
+```
+
+## INIT DATABASE
+
+```shell
+$ code-push-server-db init --dbhost "your mysql host" --dbport "your mysql port" --dbuser "your mysql user" --dbpassword "your mysql password"
+```
+
+or from source code
+
+```shell
+$ ./bin/db init --dbhost "your mysql host" --dbport "your mysql port" --dbuser "your mysql user" --dbpassword "your mysql password"
+```
+
+> output: success
+
+## CONFIGURE for code-push-server
+
+save the file [config.js](https://github.com/lisong/code-push-server/blob/master/config/config.js)
+
+some config have to change:
+
+- `local`.`storageDir` change to your directory,make sure have read/write permissions.
+- `local`.`downloadUrl` replace `127.0.0.1` to your machine ip.
+- `common`.`dataDir` change to your directory,make sure have read/write permissions.
+- `jwt`.`tokenSecret` get the random string from `https://www.grc.com/passwords.htm`, and replace the value `INSERT_RANDOM_TOKEN_KEY`.
+- `db` config: `username`,`password`,`host`,`port` change your own's
+
+## CONFIGURE for pm2
+
+save the file [process.json](https://github.com/lisong/code-push-server/blob/master/docs/process.json)
+
+some config have to change:
+
+- `script` if you install code-push-server from npm use `code-push-server`,or use `"your source code dir"/bin/www`
+- `CONFIG_FILE` above config.js file path,use absolute path.
+
+## START SERVICE
+
+```shell
+$ pm2 start process.json
+```
+
+## RESTART SERVICE
+
+```shell
+$ pm2 restart process.json
+```
+
+## STOP SERVICE
+
+```shell
+$ pm2 stop process.json
+```
+
+## CHECK SERVICE IS OK
+
+```shell
+$ curl -I http://YOUR_CODE_PUSH_SERVER_IP:3000/
+```
+
+> return httpCode `200 OK`
+
+```http
+HTTP/1.1 200 OK
+X-DNS-Prefetch-Control: off
+X-Frame-Options: SAMEORIGIN
+Strict-Transport-Security: max-age=15552000; includeSubDomains
+X-Download-Options: noopen
+X-Content-Type-Options: nosniff
+X-XSS-Protection: 1; mode=block
+Content-Type: text/html; charset=utf-8
+Content-Length: 592
+ETag: W/"250-IiCMcM1ZUFSswSYCU0KeFYFEMO8"
+Date: Sat, 25 Aug 2018 15:45:46 GMT
+Connection: keep-alive
+```
+
+
+## Use redis impove concurrent and security
+
+> config redis in config.js
+
+- `updateCheckCache`
+- `rolloutClientUniqueIdCache`
+- `tryLoginTimes`
+
+
+## UPGRADE
+
+*from npm package*
+
+```shell
+$ npm install -g code-push-server@latest
+$ code-push-server-db upgrade --dbhost "your mysql host" --dbport "your mysql port" --dbuser "your mysql user" --dbpassword "your mysql password" # upgrade codepush database
+$ pm2 restart code-push-server # restart service
+```
+
+*from source code*
+
+```shell
+$ cd /path/to/code-push-server
+$ git pull --rebase origin master
+$ ./bin/db upgrade --dbhost "your mysql host" --dbport "your mysql port" --dbuser "your mysql user" --dbpassword "your mysql password"
+# upgrade codepush database
+$ pm2 restart code-push-server # restart service
+```
+
+
+## view pm2 logs
+
+```shell
+$ pm2 ls
+$ pm2 show code-push-server
+$ tail -f "output file path"
+```
+
+
+## Support Storage mode
+
+- local (default)
+- qiniu (qiniu)
+- s3 (aws)
+- oss (aliyun)
+- tencentcloud
+
+## Default listen Host/Port 0.0.0.0/3000
+
+> you can change it in process.json, env: PORT,HOST
+
+
+## [code-push-cli](https://github.com/Microsoft/code-push)
+
+> Use code-push-cli manager CodePushServer
+
+```shell
+$ npm install code-push-cli@latest -g
+$ code-push login http://YOU_SERVICE_IP:3000 #login in browser account:admin password:123456
+```
+
+> change admin password eg.
+
+```shell
+$ curl -X PATCH -H "Authorization: Bearer mytoken" -H "Accept: application/json" -H "Content-Type:application/json" -d '{"oldPassword":"123456","newPassword":"654321"}' http://YOU_SERVICE_IP:3000/users/password
+```
+
+
+## config react-native project
+
+> Follow the react-native-code-push docs, addition iOS add a new entry named CodePushServerURL, whose value is the key of ourself CodePushServer URL. Android use the new CodePush constructor in MainApplication point CodePushServerUrl
+
+iOS eg. in file Info.plist
+
+```xml
+...
+CodePushDeploymentKey
+YourCodePushKey
+CodePushServerURL
+YourCodePushServerUrl
+...
+```
+
+Android eg. in file MainApplication.java
+
+```java
+@Override
+protected List getPackages() {
+ return Arrays.asList(
+ new MainReactPackage(),
+ new CodePush(
+ "YourKey",
+ MainApplication.this,
+ BuildConfig.DEBUG,
+ "YourCodePushServerUrl"
+ )
+ );
+}
+```
+
+
+## [cordova-plugin-code-push](https://github.com/Microsoft/cordova-plugin-code-push) for cordova
+
+```shell
+$ cd /path/to/project
+$ cordova plugin add cordova-plugin-code-push@latest --save
+```
+
+## config cordova project
+
+edit config.xml. add code below.
+
+```xml
+
+
+
+
+
+
+
+
+```
diff --git a/docs/process.json b/docs/process.json
index de88022b..8ef0e3e1 100644
--- a/docs/process.json
+++ b/docs/process.json
@@ -10,6 +10,14 @@
"NODE_ENV" : "production",
"PORT" : 3000,
"CONFIG_FILE" : "/path/to/production/config.js"
+
+ // Must set add config when STORAGE_TYPE is upyun
+ // "STORAGE_TYPE" : "upyun",
+ // "DOWNLOAD_URL" : "",
+ // "UPYUN_STORAGE_DIR" : "",
+ // "UPYUN_SERVICE_NAME" : "",
+ // "UPYUN_OPERATOR_NAME" : "",
+ // "UPYUN_OPERATOR_PASS" : ""
}
}
]
diff --git a/docs/react-native-code-push.md b/docs/react-native-code-push.md
index 3dce5fe1..0b70bf37 100644
--- a/docs/react-native-code-push.md
+++ b/docs/react-native-code-push.md
@@ -36,26 +36,23 @@ $ npm install code-push-cli@latest -g
```shell
$ react-native init CodePushDemo #初始化一个react-native项目
$ cd CodePushDemo
-$ npm install --save react-native-code-push #安装react-native-code-push
+$ npm install --save react-native-code-push@latest #安装react-native-code-push
$ react-native link react-native-code-push #连接到项目中,提示输入配置可以先行忽略
```
-#### 4. [code-push-server](https://github.com/lisong/code-push-server) 微软云服务在中国太慢,可以用它搭建自己的服务端。具体配置参考该项目
+#### 4. [code-push-server](https://github.com/lisong/code-push-server) 微软云服务在中国太慢,可以用它搭建自己的服务端。
-```shell
-$ npm install code-push-server -g
-$ code-push-server-db init --dbhost localhost --dbuser root --dbpassword #初始化数据库
-$ code-push-server #启动服务 浏览器中打开 http://127.0.0.1:3000
-```
+- [docker](https://github.com/lisong/code-push-server/blob/master/docker/README.md) (recommend)
+- [manual operation](https://github.com/lisong/code-push-server/blob/master/docs/README.md)
## 创建服务端应用
基于code-push-server服务
```shell
-$ code-push login http://127.0.0.1:3000 #浏览器中登录获取token,用户名:admin, 密码:123456
-$ code-push app add CodePushDemo-ios #创建iOS版, 获取Production DeploymentKey
-$ code-push app add CodePushDemo-android #创建android版,获取获取Production DeploymentKey
+$ code-push login http://YOUR_CODE_PUSH_SERVER_IP:3000 #浏览器中登录获取token,用户名:admin, 密码:123456
+$ code-push app add CodePushDemoiOS ios react-native #创建iOS版, 获取Production DeploymentKey
+$ code-push app add CodePushDemoAndroid android react-native #创建android版,获取获取Production DeploymentKey
```
## 配置CodePushDemo react-native项目
@@ -66,7 +63,7 @@ $ code-push app add CodePushDemo-android #创建android版,获取获取Product
1. `CodePushDeploymentKey`值设置为CodePushDemo-ios的Production DeploymentKey值。
-2. `CodePushServerURL`值设置为code-push-server服务地址 http://127.0.0.1:3000/ 不在同一台机器的时候,请将127.0.0.1改成外网ip或者域名地址。
+2. `CodePushServerURL`值设置为code-push-server服务地址 http://YOUR_CODE_PUSH_SERVER_IP:3000/ 不在同一台机器的时候,请将YOUR_CODE_PUSH_SERVER_IP改成外网ip或者域名地址。
3. 将默认版本号1.0改成三位1.0.0
@@ -85,12 +82,10 @@ $ code-push app add CodePushDemo-android #创建android版,获取获取Product
1. `YourKey`替换成CodePushDemo-android的Production DeploymentKey值
-2. `YourCodePushServerUrl`值设置为code-push-server服务地址 http://127.0.0.1:3000/ 不在同一台机器的时候,请将127.0.0.1改成外网ip或者域名地址。
+2. `YourCodePushServerUrl`值设置为code-push-server服务地址 http://YOUR_CODE_PUSH_SERVER_IP:3000/ 不在同一台机器的时候,请将YOUR_CODE_PUSH_SERVER_IP改成外网ip或者域名地址。
3. 将默认版本号1.0改成三位1.0.0
-4. android模拟器和code-push-server在同一台机器上时,需要额外运行命令`adb reverse tcp:3000 tcp:3000` 代理端口,否则无法访问127.0.0.1:3000端口
-
```java
@Override
protected List getPackages() {
@@ -108,7 +103,7 @@ protected List getPackages() {
## 添加更新检查
-可以参考[demo.js](https://github.com/lisong/code-push-demo-app/blob/master/demo.js)
+可以参考[code-push-demo-app](https://github.com/lisong/code-push-demo-app/)
可以在入口componentDidMount添加
```javascript
@@ -124,16 +119,6 @@ CodePush.sync({
import CodePush from "react-native-code-push"
```
-> notice:
->
-> demo.js中用到ECMAScript中Decorators语法,需要安装`$ npm install babel-preset-react-native-stage-0 --save`,
-> 同时在.babelrc中添加'react-native-stage-0/decorator-support'.
->
-> eg.
-> {
-> "presets": ["react-native", "react-native-stage-0/decorator-support"]
-> }
-
## 运行CodePushDemo react-native项目
#### iOS
@@ -163,16 +148,11 @@ $ code-push release-react CodePushDemo-ios ios -d Production #iOS版
$ code-push release-react CodePushDemo-android android -d Production #android版
```
+## 例子
-## 注意事项
+[code-push-demo-app](https://github.com/lisong/code-push-demo-app)
-- 苹果允许使用热更新[Apple's developer agreement](https://developer.apple.com/programs/ios/information/iOS_Program_Information_4_3_15.pdf), 但是规定不能弹框提示用户更新,影响用户体验。 而Google Play恰好相反,必须弹框告知用户更新。然而中国的android市场都必须关闭更新弹框,否则会在审核应用时以“请上传最新版本的二进制应用包”驳回应用。
-- react-native 不同平台bundle包不一样,在使用code-push-server的时候必须创建不同的应用来区分(eg. CodePushDemo-ios 和 CodePushDemo-android)
-- react-native-code-push只更新资源文件,不会更新java和Objective C,所以npm升级依赖包版本的时候,如果依赖包使用的本地化实现, 这时候必须更改应用版本号(ios修改Info.plist中的CFBundleShortVersionString, android修改build.gradle中的versionName), 然后重新编译app发布到应用商店。
-- 推荐使用code-push release-react 命令发布应用,该命令合并了打包和发布命令(eg. code-push release-react CodePushDemo-ios ios -d Production)
-- 每次向App Store提交新的版本时,也应该基于该提交版本同时向code-push-server发布一个初始版本。(因为后面每次向code-push-server发布版本时,code-puse-server都会和初始版本比较,生成补丁版本)
-## 例子
+### 更多信息参考[code-push-server](https://github.com/lisong/code-push-server)
-[code-push-demo-app](https://github.com/lisong/code-push-demo-app)
diff --git a/models/apps.js b/models/apps.js
index 3261bc6e..00e0372f 100644
--- a/models/apps.js
+++ b/models/apps.js
@@ -10,6 +10,9 @@ module.exports = function(sequelize, DataTypes) {
},
name: DataTypes.STRING,
uid: DataTypes.BIGINT(20),
+ os: DataTypes.INTEGER(3),
+ platform: DataTypes.INTEGER(3),
+ is_use_diff_text: DataTypes.INTEGER(3),
created_at: DataTypes.DATE,
updated_at: DataTypes.DATE,
}, {
diff --git a/models/collaborators.js b/models/collaborators.js
index 6440b358..c5f8d268 100644
--- a/models/collaborators.js
+++ b/models/collaborators.js
@@ -16,17 +16,14 @@ module.exports = function(sequelize, DataTypes) {
}, {
tableName: 'collaborators',
underscored: true,
- paranoid: true,
- classMethods: {
- findByAppNameAndUid: function(uid, appName) {
- var sql = "SELECT b.* FROM `apps` as a left join `collaborators` as b on (a.id = b.appid) where a.name= :appName and b.uid = :uid and a.`deleted_at` IS NULL and b.`deleted_at` IS NULL limit 0,1";
- return sequelize.query(sql, { replacements: { appName: appName, uid: uid }, model: Collaborators})
- .then(function(data) {
- return data.pop();
- });
- }
- }
-
+ paranoid: true
});
+ Collaborators.findByAppNameAndUid = function (uid, appName) {
+ var sql = "SELECT b.* FROM `apps` as a left join `collaborators` as b on (a.id = b.appid) where a.name= :appName and b.uid = :uid and a.`deleted_at` IS NULL and b.`deleted_at` IS NULL limit 0,1";
+ return sequelize.query(sql, { replacements: { appName: appName, uid: uid }, model: Collaborators})
+ .then(function(data) {
+ return data.pop();
+ });
+ };
return Collaborators;
};
diff --git a/models/deployments.js b/models/deployments.js
index 122b1b36..fade4b93 100644
--- a/models/deployments.js
+++ b/models/deployments.js
@@ -22,25 +22,24 @@ module.exports = function(sequelize, DataTypes) {
}, {
tableName: 'deployments',
underscored: true,
- paranoid: true,
- classMethods: {
- generateLabelId: function(deploymentId) {
- var self = this;
- return sequelize.transaction(function (t) {
- return self.findById(deploymentId, {transaction: t,lock: t.LOCK.UPDATE}).then(function (data) {
- if (_.isEmpty(data)){
- throw new AppError.AppError("does not find deployment");
- }
- data.label_id = data.label_id + 1;
- return data.save({transaction: t})
- .then(function (data) {
- return data.label_id;
- });
- });
- });
- }
- }
+ paranoid: true
});
+ Deployments.generateLabelId = function(deploymentId) {
+ var self = this;
+ return sequelize.transaction(function (t) {
+ return self.findById(deploymentId, {transaction: t,lock: t.LOCK.UPDATE}).then(function (data) {
+ if (_.isEmpty(data)){
+ throw new AppError.AppError("does not find deployment");
+ }
+ data.label_id = data.label_id + 1;
+ return data.save({transaction: t})
+ .then(function (data) {
+ return data.label_id;
+ });
+ });
+ });
+ };
+
return Deployments;
};
diff --git a/models/deployments_versions.js b/models/deployments_versions.js
index d42d06dc..41105ec8 100644
--- a/models/deployments_versions.js
+++ b/models/deployments_versions.js
@@ -11,6 +11,8 @@ module.exports = function(sequelize, DataTypes) {
deployment_id: DataTypes.INTEGER(10),
app_version: DataTypes.STRING,
current_package_id: DataTypes.INTEGER(10),
+ min_version: DataTypes.BIGINT(20),
+ max_version: DataTypes.BIGINT(20),
created_at: DataTypes.DATE,
updated_at: DataTypes.DATE,
}, {
diff --git a/models/log_report_deploy.js b/models/log_report_deploy.js
new file mode 100644
index 00000000..f8c39d5e
--- /dev/null
+++ b/models/log_report_deploy.js
@@ -0,0 +1,24 @@
+"use strict";
+
+module.exports = function(sequelize, DataTypes) {
+ var LogReportDeploy = sequelize.define("LogReportDeploy", {
+ id: {
+ type: DataTypes.BIGINT(20),
+ allowNull: false,
+ autoIncrement: true,
+ primaryKey: true
+ },
+ status: DataTypes.INTEGER(3),
+ package_id : DataTypes.INTEGER(10),
+ client_unique_id : DataTypes.STRING,
+ previous_label : DataTypes.STRING,
+ previous_deployment_key : DataTypes.STRING,
+ created_at: DataTypes.DATE,
+ }, {
+ tableName: 'log_report_deploy',
+ underscored: true,
+ updatedAt: false,
+ paranoid: true
+ });
+ return LogReportDeploy;
+};
diff --git a/models/log_report_download.js b/models/log_report_download.js
new file mode 100644
index 00000000..62966cf9
--- /dev/null
+++ b/models/log_report_download.js
@@ -0,0 +1,21 @@
+"use strict";
+
+module.exports = function(sequelize, DataTypes) {
+ var LogReportDownload = sequelize.define("LogReportDownload", {
+ id: {
+ type: DataTypes.BIGINT(20),
+ allowNull: false,
+ autoIncrement: true,
+ primaryKey: true
+ },
+ package_id : DataTypes.INTEGER(10),
+ client_unique_id : DataTypes.STRING,
+ created_at: DataTypes.DATE,
+ }, {
+ tableName: 'log_report_download',
+ underscored: true,
+ updatedAt: false,
+ paranoid: true
+ });
+ return LogReportDownload;
+};
diff --git a/models/packages.js b/models/packages.js
index 6ec423db..989eb32a 100644
--- a/models/packages.js
+++ b/models/packages.js
@@ -21,6 +21,8 @@ module.exports = function(sequelize, DataTypes) {
original_deployment: DataTypes.STRING,
released_by: DataTypes.STRING,
is_mandatory: DataTypes.INTEGER(3),
+ is_disabled: DataTypes.INTEGER(3),
+ rollout: DataTypes.INTEGER(3),
created_at: DataTypes.DATE,
updated_at: DataTypes.DATE,
}, {
diff --git a/models/packages_metrics.js b/models/packages_metrics.js
index 86a1acff..d95bce92 100644
--- a/models/packages_metrics.js
+++ b/models/packages_metrics.js
@@ -20,41 +20,7 @@ module.exports = function(sequelize, DataTypes) {
}, {
tableName: 'packages_metrics',
underscored: true,
- paranoid: true,
- classMethods: {
- addOne : function (packageId, fieldName) {
- var self = this;
- var sql = 'UPDATE packages_metrics SET `' + fieldName + '`=`' + fieldName + '` + 1 WHERE package_id = :package_id';
- return sequelize.query(sql, { replacements: { package_id: packageId}})
- .spread(function(results, metadata) {
- if (_.eq(results.affectedRows, 0)) {
- var params = {
- package_id: packageId,
- active: 0,
- downloaded: 0,
- failed: 0,
- installed: 0,
- };
- params[fieldName] = 1;
- return self.create(params);
- }else {
- return true;
- }
- });
- },
- addOneOnDownloadById: function (packageId) {
- return this.addOne(packageId, 'downloaded');
- },
- addOneOnFailedById: function (packageId) {
- return this.addOne(packageId, 'failed');
- },
- addOneOnInstalledById: function (packageId) {
- return this.addOne(packageId, 'installed');
- },
- addOneOnActiveById: function (packageId) {
- return this.addOne(packageId, 'active');
- },
- }
+ paranoid: true
});
return PackagesMetrics;
};
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..79668d14
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,3860 @@
+{
+ "name": "code-push-server",
+ "version": "0.5.4",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@types/babel-types": {
+ "version": "7.0.1",
+ "resolved": "http://registry.npm.taobao.org/@types/babel-types/download/@types/babel-types-7.0.1.tgz",
+ "integrity": "sha1-FAXlOWloxDAplLAWHOQFtyuHQlc="
+ },
+ "@types/babylon": {
+ "version": "6.16.2",
+ "resolved": "http://registry.npm.taobao.org/@types/babylon/download/@types/babylon-6.16.2.tgz",
+ "integrity": "sha1-BizmO2k9mvHCRvWu35KLycMFicg=",
+ "requires": {
+ "@types/babel-types": "*"
+ }
+ },
+ "@types/geojson": {
+ "version": "1.0.6",
+ "resolved": "http://registry.npm.taobao.org/@types/geojson/download/@types/geojson-1.0.6.tgz",
+ "integrity": "sha1-PgKXJyjGkkjCrwjWCkjLuGgP/98="
+ },
+ "@types/node": {
+ "version": "9.4.7",
+ "resolved": "http://registry.npm.taobao.org/@types/node/download/@types/node-9.4.7.tgz",
+ "integrity": "sha1-V9gc2YcZ3yyd4Rjy1fOxEg3NcnU="
+ },
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npm.taobao.org/abbrev/download/abbrev-1.1.1.tgz",
+ "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg="
+ },
+ "accepts": {
+ "version": "1.3.5",
+ "resolved": "http://registry.npm.taobao.org/accepts/download/accepts-1.3.5.tgz",
+ "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
+ "requires": {
+ "mime-types": "~2.1.18",
+ "negotiator": "0.6.1"
+ }
+ },
+ "acorn": {
+ "version": "3.3.0",
+ "resolved": "http://registry.npm.taobao.org/acorn/download/acorn-3.3.0.tgz",
+ "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
+ },
+ "acorn-globals": {
+ "version": "3.1.0",
+ "resolved": "http://registry.npm.taobao.org/acorn-globals/download/acorn-globals-3.1.0.tgz",
+ "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=",
+ "requires": {
+ "acorn": "^4.0.4"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "4.0.13",
+ "resolved": "http://registry.npm.taobao.org/acorn/download/acorn-4.0.13.tgz",
+ "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="
+ }
+ }
+ },
+ "address": {
+ "version": "1.0.3",
+ "resolved": "http://registry.npm.taobao.org/address/download/address-1.0.3.tgz",
+ "integrity": "sha1-tfUGMfjWzsi9IMljljr7VeBsvOk="
+ },
+ "agentkeepalive": {
+ "version": "3.3.0",
+ "resolved": "http://registry.npm.taobao.org/agentkeepalive/download/agentkeepalive-3.3.0.tgz",
+ "integrity": "sha1-bV3lgpr9O+JxIgGjknX9EcZRhXw=",
+ "requires": {
+ "humanize-ms": "^1.2.1"
+ }
+ },
+ "ajv": {
+ "version": "5.5.2",
+ "resolved": "http://registry.npm.taobao.org/ajv/download/ajv-5.5.2.tgz",
+ "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+ "requires": {
+ "co": "^4.6.0",
+ "fast-deep-equal": "^1.0.0",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.3.0"
+ }
+ },
+ "align-text": {
+ "version": "0.1.4",
+ "resolved": "http://registry.npm.taobao.org/align-text/download/align-text-0.1.4.tgz",
+ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+ "requires": {
+ "kind-of": "^3.0.2",
+ "longest": "^1.0.1",
+ "repeat-string": "^1.5.2"
+ }
+ },
+ "aliyun-oss-upload-stream": {
+ "version": "1.3.0",
+ "resolved": "http://registry.npm.taobao.org/aliyun-oss-upload-stream/download/aliyun-oss-upload-stream-1.3.0.tgz",
+ "integrity": "sha1-ODAbGfA0QGhDjrY5d6DNldYEcMA="
+ },
+ "aliyun-sdk": {
+ "version": "1.11.10",
+ "resolved": "http://registry.npm.taobao.org/aliyun-sdk/download/aliyun-sdk-1.11.10.tgz",
+ "integrity": "sha1-ZDvH+GDrv08F7mmfoGp05eQR8lA=",
+ "requires": {
+ "node_memcached": "1.1.3",
+ "pomelo-protobuf": "^0.4.0",
+ "protobufjs": "^4.1.2",
+ "xml2js": "0.4.4",
+ "xmlbuilder": "^2.4.5"
+ }
+ },
+ "ambi": {
+ "version": "2.5.0",
+ "resolved": "http://registry.npm.taobao.org/ambi/download/ambi-2.5.0.tgz",
+ "integrity": "sha1-fI43K+SIkRV+fOoBy2+RQ9H3QiA=",
+ "requires": {
+ "editions": "^1.1.1",
+ "typechecker": "^4.3.0"
+ },
+ "dependencies": {
+ "typechecker": {
+ "version": "4.5.0",
+ "resolved": "http://registry.npm.taobao.org/typechecker/download/typechecker-4.5.0.tgz",
+ "integrity": "sha1-w4KSAJeBI2S7r0WVsKtliCRBF6Y=",
+ "requires": {
+ "editions": "^1.3.4"
+ }
+ }
+ }
+ },
+ "amdefine": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/amdefine/download/amdefine-1.0.1.tgz",
+ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "http://registry.npm.taobao.org/ansi-regex/download/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+ },
+ "ansicolors": {
+ "version": "0.2.1",
+ "resolved": "http://registry.npm.taobao.org/ansicolors/download/ansicolors-0.2.1.tgz",
+ "integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8="
+ },
+ "any-promise": {
+ "version": "1.3.0",
+ "resolved": "http://registry.npm.taobao.org/any-promise/download/any-promise-1.3.0.tgz",
+ "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "http://registry.npm.taobao.org/argparse/download/argparse-1.0.10.tgz",
+ "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=",
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ },
+ "dependencies": {
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "http://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
+ }
+ }
+ },
+ "array-flatten": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npm.taobao.org/array-flatten/download/array-flatten-1.1.1.tgz",
+ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+ },
+ "asap": {
+ "version": "2.0.6",
+ "resolved": "http://registry.npm.taobao.org/asap/download/asap-2.0.6.tgz",
+ "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
+ },
+ "ascli": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/ascli/download/ascli-1.0.1.tgz",
+ "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=",
+ "requires": {
+ "colour": "~0.7.1",
+ "optjs": "~3.2.2"
+ }
+ },
+ "asn1": {
+ "version": "0.2.3",
+ "resolved": "http://registry.npm.taobao.org/asn1/download/asn1-0.2.3.tgz",
+ "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/assert-plus/download/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+ },
+ "async": {
+ "version": "1.5.2",
+ "resolved": "http://registry.npm.taobao.org/async/download/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "http://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+ },
+ "aws-sdk": {
+ "version": "2.211.0",
+ "resolved": "http://registry.npm.taobao.org/aws-sdk/download/aws-sdk-2.211.0.tgz",
+ "integrity": "sha1-kCt1Jedv+fpyzQOLc7ikfIxnGmI=",
+ "requires": {
+ "buffer": "4.9.1",
+ "events": "^1.1.1",
+ "jmespath": "0.15.0",
+ "querystring": "0.2.0",
+ "sax": "1.2.1",
+ "url": "0.10.3",
+ "uuid": "3.1.0",
+ "xml2js": "0.4.17",
+ "xmlbuilder": "4.2.1"
+ },
+ "dependencies": {
+ "sax": {
+ "version": "1.2.1",
+ "resolved": "http://registry.npm.taobao.org/sax/download/sax-1.2.1.tgz",
+ "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
+ },
+ "xml2js": {
+ "version": "0.4.17",
+ "resolved": "http://registry.npm.taobao.org/xml2js/download/xml2js-0.4.17.tgz",
+ "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=",
+ "requires": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "^4.1.0"
+ }
+ },
+ "xmlbuilder": {
+ "version": "4.2.1",
+ "resolved": "http://registry.npm.taobao.org/xmlbuilder/download/xmlbuilder-4.2.1.tgz",
+ "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=",
+ "requires": {
+ "lodash": "^4.0.0"
+ }
+ }
+ }
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "http://registry.npm.taobao.org/aws-sign2/download/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
+ },
+ "aws4": {
+ "version": "1.6.0",
+ "resolved": "http://registry.npm.taobao.org/aws4/download/aws4-1.6.0.tgz",
+ "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4="
+ },
+ "axios": {
+ "version": "0.18.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz",
+ "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==",
+ "requires": {
+ "follow-redirects": "1.5.10",
+ "is-buffer": "^2.0.2"
+ },
+ "dependencies": {
+ "is-buffer": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
+ "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw=="
+ }
+ }
+ },
+ "babel-runtime": {
+ "version": "6.26.0",
+ "resolved": "http://registry.npm.taobao.org/babel-runtime/download/babel-runtime-6.26.0.tgz",
+ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+ "requires": {
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
+ }
+ },
+ "babel-types": {
+ "version": "6.26.0",
+ "resolved": "http://registry.npm.taobao.org/babel-types/download/babel-types-6.26.0.tgz",
+ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.4",
+ "to-fast-properties": "^1.0.3"
+ }
+ },
+ "babylon": {
+ "version": "6.18.0",
+ "resolved": "http://registry.npm.taobao.org/babylon/download/babylon-6.18.0.tgz",
+ "integrity": "sha1-ry87iPpvXB5MY00aD46sT1WzleM="
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+ },
+ "base-64": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
+ "integrity": "sha1-eAqZyE59YAJgNhURxId2E78k9rs="
+ },
+ "base64-js": {
+ "version": "1.2.3",
+ "resolved": "http://registry.npm.taobao.org/base64-js/download/base64-js-1.2.3.tgz",
+ "integrity": "sha1-+xNmgjPZYUz1+0vOlam6QJbN+AE="
+ },
+ "base64url": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/base64url/download/base64url-2.0.0.tgz",
+ "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs="
+ },
+ "basic-auth": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/basic-auth/download/basic-auth-2.0.0.tgz",
+ "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=",
+ "requires": {
+ "safe-buffer": "5.1.1"
+ }
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/bcrypt-pbkdf/download/bcrypt-pbkdf-1.0.1.tgz",
+ "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
+ "optional": true,
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "bcryptjs": {
+ "version": "2.4.3",
+ "resolved": "http://registry.npm.taobao.org/bcryptjs/download/bcryptjs-2.4.3.tgz",
+ "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
+ },
+ "bluebird": {
+ "version": "3.5.1",
+ "resolved": "http://registry.npm.taobao.org/bluebird/download/bluebird-3.5.1.tgz",
+ "integrity": "sha1-2VUfnemPH82h5oPRfukaBgLuLrk="
+ },
+ "body-parser": {
+ "version": "1.18.2",
+ "resolved": "http://registry.npm.taobao.org/body-parser/download/body-parser-1.18.2.tgz",
+ "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
+ "requires": {
+ "bytes": "3.0.0",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "~1.1.1",
+ "http-errors": "~1.6.2",
+ "iconv-lite": "0.4.19",
+ "on-finished": "~2.3.0",
+ "qs": "6.5.1",
+ "raw-body": "2.3.2",
+ "type-is": "~1.6.15"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz",
+ "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "boom": {
+ "version": "4.3.1",
+ "resolved": "http://registry.npm.taobao.org/boom/download/boom-4.3.1.tgz",
+ "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
+ "requires": {
+ "hoek": "4.x.x"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "http://registry.npm.taobao.org/brace-expansion/download/brace-expansion-1.1.11.tgz",
+ "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=",
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "browser-stdout": {
+ "version": "1.3.0",
+ "resolved": "http://registry.npm.taobao.org/browser-stdout/download/browser-stdout-1.3.0.tgz",
+ "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=",
+ "dev": true
+ },
+ "buffer": {
+ "version": "4.9.1",
+ "resolved": "http://registry.npm.taobao.org/buffer/download/buffer-4.9.1.tgz",
+ "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
+ "requires": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4",
+ "isarray": "^1.0.0"
+ }
+ },
+ "buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "http://registry.npm.taobao.org/buffer-crc32/download/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
+ },
+ "buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/buffer-equal-constant-time/download/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+ },
+ "bufferview": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/bufferview/download/bufferview-1.0.1.tgz",
+ "integrity": "sha1-ev10pF+Tf6QiodM4wIu/3HbNcl0="
+ },
+ "bytebuffer": {
+ "version": "4.1.0",
+ "resolved": "http://registry.npm.taobao.org/bytebuffer/download/bytebuffer-4.1.0.tgz",
+ "integrity": "sha1-TFgmngUqseSx9/82T9+zzogpBqo=",
+ "requires": {
+ "bufferview": "~1",
+ "long": "~2 >=2.3.0"
+ }
+ },
+ "bytes": {
+ "version": "3.0.0",
+ "resolved": "http://registry.npm.taobao.org/bytes/download/bytes-3.0.0.tgz",
+ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
+ },
+ "camelcase": {
+ "version": "2.1.1",
+ "resolved": "http://registry.npm.taobao.org/camelcase/download/camelcase-2.1.1.tgz",
+ "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
+ },
+ "camelize": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/camelize/download/camelize-1.0.0.tgz",
+ "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
+ },
+ "cardinal": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/cardinal/download/cardinal-1.0.0.tgz",
+ "integrity": "sha1-UOIcGwqjdyn5N33vGWtanOyTLuk=",
+ "requires": {
+ "ansicolors": "~0.2.1",
+ "redeyed": "~1.0.0"
+ }
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "http://registry.npm.taobao.org/caseless/download/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+ },
+ "center-align": {
+ "version": "0.1.3",
+ "resolved": "http://registry.npm.taobao.org/center-align/download/center-align-0.1.3.tgz",
+ "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
+ "requires": {
+ "align-text": "^0.1.3",
+ "lazy-cache": "^1.0.3"
+ }
+ },
+ "character-parser": {
+ "version": "2.2.0",
+ "resolved": "http://registry.npm.taobao.org/character-parser/download/character-parser-2.2.0.tgz",
+ "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=",
+ "requires": {
+ "is-regex": "^1.0.3"
+ }
+ },
+ "charenc": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+ "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
+ },
+ "circular-json": {
+ "version": "0.5.5",
+ "resolved": "http://registry.npm.taobao.org/circular-json/download/circular-json-0.5.5.tgz",
+ "integrity": "sha1-ZBgu81kELTfNjnZ/yd6Hix6UR9M="
+ },
+ "clean-css": {
+ "version": "3.4.28",
+ "resolved": "http://registry.npm.taobao.org/clean-css/download/clean-css-3.4.28.tgz",
+ "integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=",
+ "requires": {
+ "commander": "2.8.x",
+ "source-map": "0.4.x"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.8.1",
+ "resolved": "http://registry.npm.taobao.org/commander/download/commander-2.8.1.tgz",
+ "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=",
+ "requires": {
+ "graceful-readlink": ">= 1.0.0"
+ }
+ },
+ "source-map": {
+ "version": "0.4.4",
+ "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.4.4.tgz",
+ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ }
+ }
+ },
+ "cliui": {
+ "version": "3.2.0",
+ "resolved": "http://registry.npm.taobao.org/cliui/download/cliui-3.2.0.tgz",
+ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
+ "requires": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wrap-ansi": "^2.0.0"
+ }
+ },
+ "cls-bluebird": {
+ "version": "2.1.0",
+ "resolved": "http://registry.npm.taobao.org/cls-bluebird/download/cls-bluebird-2.1.0.tgz",
+ "integrity": "sha1-N+8eCAqP+1XC9BZPU28ZGeeWiu4=",
+ "requires": {
+ "is-bluebird": "^1.0.2",
+ "shimmer": "^1.1.0"
+ }
+ },
+ "co": {
+ "version": "4.6.0",
+ "resolved": "http://registry.npm.taobao.org/co/download/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npm.taobao.org/code-point-at/download/code-point-at-1.1.0.tgz",
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+ },
+ "coffee-script": {
+ "version": "1.12.7",
+ "resolved": "http://registry.npm.taobao.org/coffee-script/download/coffee-script-1.12.7.tgz",
+ "integrity": "sha1-wF2uDLeVkdBbMHCoQzqYyaiczFM="
+ },
+ "colour": {
+ "version": "0.7.1",
+ "resolved": "http://registry.npm.taobao.org/colour/download/colour-0.7.1.tgz",
+ "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g="
+ },
+ "combined-stream": {
+ "version": "1.0.6",
+ "resolved": "http://registry.npm.taobao.org/combined-stream/download/combined-stream-1.0.6.tgz",
+ "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "http://registry.npm.taobao.org/component-emitter/download/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "http://registry.npm.taobao.org/concat-map/download/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ },
+ "concat-stream": {
+ "version": "1.6.0",
+ "resolved": "http://registry.npm.taobao.org/concat-stream/download/concat-stream-1.6.0.tgz",
+ "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
+ "requires": {
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "configstore": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz",
+ "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==",
+ "requires": {
+ "dot-prop": "^4.1.0",
+ "graceful-fs": "^4.1.2",
+ "make-dir": "^1.0.0",
+ "unique-string": "^1.0.0",
+ "write-file-atomic": "^2.0.0",
+ "xdg-basedir": "^3.0.0"
+ }
+ },
+ "constantinople": {
+ "version": "3.1.2",
+ "resolved": "http://registry.npm.taobao.org/constantinople/download/constantinople-3.1.2.tgz",
+ "integrity": "sha1-1F7XJPV9PRBQABen06iJwTga5kc=",
+ "requires": {
+ "@types/babel-types": "^7.0.0",
+ "@types/babylon": "^6.16.2",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0"
+ }
+ },
+ "content-disposition": {
+ "version": "0.5.2",
+ "resolved": "http://registry.npm.taobao.org/content-disposition/download/content-disposition-0.5.2.tgz",
+ "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
+ },
+ "content-security-policy-builder": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/content-security-policy-builder/download/content-security-policy-builder-2.0.0.tgz",
+ "integrity": "sha1-h0mh1UL8voIjcoHqn3Fs5os5TdI="
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "http://registry.npm.taobao.org/content-type/download/content-type-1.0.4.tgz",
+ "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js="
+ },
+ "cookie": {
+ "version": "0.3.1",
+ "resolved": "http://registry.npm.taobao.org/cookie/download/cookie-0.3.1.tgz",
+ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
+ },
+ "cookie-parser": {
+ "version": "1.4.3",
+ "resolved": "http://registry.npm.taobao.org/cookie-parser/download/cookie-parser-1.4.3.tgz",
+ "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=",
+ "requires": {
+ "cookie": "0.3.1",
+ "cookie-signature": "1.0.6"
+ }
+ },
+ "cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "http://registry.npm.taobao.org/cookie-signature/download/cookie-signature-1.0.6.tgz",
+ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+ },
+ "cookiejar": {
+ "version": "2.1.1",
+ "resolved": "http://registry.npm.taobao.org/cookiejar/download/cookiejar-2.1.1.tgz",
+ "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o=",
+ "dev": true
+ },
+ "core-js": {
+ "version": "2.5.3",
+ "resolved": "http://registry.npm.taobao.org/core-js/download/core-js-2.5.3.tgz",
+ "integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4="
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npm.taobao.org/core-util-is/download/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+ },
+ "cos-nodejs-sdk-v5": {
+ "version": "2.4.10",
+ "resolved": "https://registry.npmjs.org/cos-nodejs-sdk-v5/-/cos-nodejs-sdk-v5-2.4.10.tgz",
+ "integrity": "sha512-espPlCyyOc4vBKKI6Mwb5ZKzjYDgd0GOK4H0msiryupc3wlQCfPqJM+PubVlUEssqK58wIiV9Bq4Bo/ts2irjg==",
+ "requires": {
+ "configstore": "^3.1.2",
+ "qcloudapi-sdk": "^0.2.0",
+ "request": "^2.81.0",
+ "xml2js": "^0.4.19"
+ },
+ "dependencies": {
+ "xml2js": {
+ "version": "0.4.19",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
+ "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
+ "requires": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~9.0.1"
+ }
+ },
+ "xmlbuilder": {
+ "version": "9.0.7",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
+ "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
+ }
+ }
+ },
+ "crc32": {
+ "version": "0.2.2",
+ "resolved": "http://registry.npm.taobao.org/crc32/download/crc32-0.2.2.tgz",
+ "integrity": "sha1-etIg1v/c0Rn5/BJ6d3LKzqOQpLo="
+ },
+ "cross-spawn": {
+ "version": "5.1.0",
+ "resolved": "http://registry.npm.taobao.org/cross-spawn/download/cross-spawn-5.1.0.tgz",
+ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+ "requires": {
+ "lru-cache": "^4.0.1",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "crypt": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+ "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
+ },
+ "cryptiles": {
+ "version": "3.1.2",
+ "resolved": "http://registry.npm.taobao.org/cryptiles/download/cryptiles-3.1.2.tgz",
+ "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
+ "requires": {
+ "boom": "5.x.x"
+ },
+ "dependencies": {
+ "boom": {
+ "version": "5.2.0",
+ "resolved": "http://registry.npm.taobao.org/boom/download/boom-5.2.0.tgz",
+ "integrity": "sha1-XdnabuOl8wIHdDYpDLcX0/SlTgI=",
+ "requires": {
+ "hoek": "4.x.x"
+ }
+ }
+ }
+ },
+ "crypto-random-string": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
+ "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4="
+ },
+ "csextends": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npm.taobao.org/csextends/download/csextends-1.1.1.tgz",
+ "integrity": "sha1-zFPBNJ+vfwrmzfb2xKTZFW08TsE=",
+ "requires": {
+ "coffee-script": "^1.12.5"
+ }
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "http://registry.npm.taobao.org/dashdash/download/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "dasherize": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/dasherize/download/dasherize-2.0.0.tgz",
+ "integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg="
+ },
+ "date-format": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npm.taobao.org/date-format/download/date-format-1.2.0.tgz",
+ "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg="
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz",
+ "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npm.taobao.org/decamelize/download/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "http://registry.npm.taobao.org/deep-is/download/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "default-user-agent": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/default-user-agent/download/default-user-agent-1.0.0.tgz",
+ "integrity": "sha1-FsRu/cq6PtxF8k8r1IaLAbfCrcY=",
+ "requires": {
+ "os-name": "~1.0.3"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/delayed-stream/download/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+ },
+ "denque": {
+ "version": "1.2.3",
+ "resolved": "http://registry.npm.taobao.org/denque/download/denque-1.2.3.tgz",
+ "integrity": "sha1-mMUMjdjN+uMYzFhZzI7j2g+bDMI="
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "http://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+ },
+ "destroy": {
+ "version": "1.0.4",
+ "resolved": "http://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+ },
+ "diff": {
+ "version": "3.2.0",
+ "resolved": "http://registry.npm.taobao.org/diff/download/diff-3.2.0.tgz",
+ "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=",
+ "dev": true
+ },
+ "diff-match-patch": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.1.tgz",
+ "integrity": "sha512-A0QEhr4PxGUMEtKxd6X+JLnOTFd3BfIPSDpsc4dMvj+CbSaErDwTpoTo/nFJDMSrjxLW4BiNq+FbNisAAHhWeQ=="
+ },
+ "digest-header": {
+ "version": "0.0.1",
+ "resolved": "http://registry.npm.taobao.org/digest-header/download/digest-header-0.0.1.tgz",
+ "integrity": "sha1-Ecz23uxXZqw3l0TZAcEsuklRS+Y=",
+ "requires": {
+ "utility": "0.1.11"
+ }
+ },
+ "dns-prefetch-control": {
+ "version": "0.1.0",
+ "resolved": "http://registry.npm.taobao.org/dns-prefetch-control/download/dns-prefetch-control-0.1.0.tgz",
+ "integrity": "sha1-YN20V3dOF48flBXwyrsOhbCzALI="
+ },
+ "doctypes": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npm.taobao.org/doctypes/download/doctypes-1.1.0.tgz",
+ "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk="
+ },
+ "dont-sniff-mimetype": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/dont-sniff-mimetype/download/dont-sniff-mimetype-1.0.0.tgz",
+ "integrity": "sha1-WTKJDcn04vGeXrAqIAJuXl78j1g="
+ },
+ "dot-prop": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
+ "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==",
+ "requires": {
+ "is-obj": "^1.0.0"
+ }
+ },
+ "dot-qs": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/dot-qs/-/dot-qs-0.2.0.tgz",
+ "integrity": "sha1-02UX/iS3zaYfznpQJqACSvr1pDk="
+ },
+ "dottie": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/dottie/download/dottie-2.0.0.tgz",
+ "integrity": "sha1-2hkZgci41xPKARXViYzzl8Lw3dA="
+ },
+ "double-ended-queue": {
+ "version": "2.1.0-0",
+ "resolved": "http://registry.npm.taobao.org/double-ended-queue/download/double-ended-queue-2.1.0-0.tgz",
+ "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw="
+ },
+ "eachr": {
+ "version": "2.0.4",
+ "resolved": "http://registry.npm.taobao.org/eachr/download/eachr-2.0.4.tgz",
+ "integrity": "sha1-Rm98qhBwj2EFCeMsgHqv5X/BIr8=",
+ "requires": {
+ "typechecker": "^2.0.8"
+ }
+ },
+ "ecc-jsbn": {
+ "version": "0.1.1",
+ "resolved": "http://registry.npm.taobao.org/ecc-jsbn/download/ecc-jsbn-0.1.1.tgz",
+ "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
+ "optional": true,
+ "requires": {
+ "jsbn": "~0.1.0"
+ }
+ },
+ "ecdsa-sig-formatter": {
+ "version": "1.0.9",
+ "resolved": "http://registry.npm.taobao.org/ecdsa-sig-formatter/download/ecdsa-sig-formatter-1.0.9.tgz",
+ "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=",
+ "requires": {
+ "base64url": "^2.0.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "editions": {
+ "version": "1.3.4",
+ "resolved": "http://registry.npm.taobao.org/editions/download/editions-1.3.4.tgz",
+ "integrity": "sha1-NmLLWSNHwxaOuOSYoP9zJx1n9Qs="
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npm.taobao.org/encodeurl/download/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+ },
+ "entities": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npm.taobao.org/entities/download/entities-1.1.1.tgz",
+ "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA="
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "http://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "http://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "resolved": "http://registry.npm.taobao.org/esutils/download/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "http://registry.npm.taobao.org/etag/download/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+ },
+ "events": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npm.taobao.org/events/download/events-1.1.1.tgz",
+ "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
+ },
+ "execa": {
+ "version": "0.7.0",
+ "resolved": "http://registry.npm.taobao.org/execa/download/execa-0.7.0.tgz",
+ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
+ "requires": {
+ "cross-spawn": "^5.0.1",
+ "get-stream": "^3.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "expect-ct": {
+ "version": "0.1.0",
+ "resolved": "http://registry.npm.taobao.org/expect-ct/download/expect-ct-0.1.0.tgz",
+ "integrity": "sha1-UnNWeN4YUwiQ2Ne5XwrGNkCVgJQ="
+ },
+ "express": {
+ "version": "4.16.3",
+ "resolved": "http://registry.npm.taobao.org/express/download/express-4.16.3.tgz",
+ "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=",
+ "requires": {
+ "accepts": "~1.3.5",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.18.2",
+ "content-disposition": "0.5.2",
+ "content-type": "~1.0.4",
+ "cookie": "0.3.1",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "1.1.1",
+ "fresh": "0.5.2",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.2",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.3",
+ "qs": "6.5.1",
+ "range-parser": "~1.2.0",
+ "safe-buffer": "5.1.1",
+ "send": "0.16.2",
+ "serve-static": "1.13.2",
+ "setprototypeof": "1.1.0",
+ "statuses": "~1.4.0",
+ "type-is": "~1.6.16",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz",
+ "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.1.0.tgz",
+ "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY="
+ }
+ }
+ },
+ "extend": {
+ "version": "3.0.1",
+ "resolved": "http://registry.npm.taobao.org/extend/download/extend-3.0.1.tgz",
+ "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
+ },
+ "extendr": {
+ "version": "2.1.0",
+ "resolved": "http://registry.npm.taobao.org/extendr/download/extendr-2.1.0.tgz",
+ "integrity": "sha1-MBqgu+pWX00tyPVw8qImEahSe1Y=",
+ "requires": {
+ "typechecker": "~2.0.1"
+ },
+ "dependencies": {
+ "typechecker": {
+ "version": "2.0.8",
+ "resolved": "http://registry.npm.taobao.org/typechecker/download/typechecker-2.0.8.tgz",
+ "integrity": "sha1-6D2oS7ZMWEzLNFg4V2xAsDN9uC4="
+ }
+ }
+ },
+ "extract-opts": {
+ "version": "2.2.0",
+ "resolved": "http://registry.npm.taobao.org/extract-opts/download/extract-opts-2.2.0.tgz",
+ "integrity": "sha1-H6KOunNSxttID4hc63GkaBC+bX0=",
+ "requires": {
+ "typechecker": "~2.0.1"
+ },
+ "dependencies": {
+ "typechecker": {
+ "version": "2.0.8",
+ "resolved": "http://registry.npm.taobao.org/typechecker/download/typechecker-2.0.8.tgz",
+ "integrity": "sha1-6D2oS7ZMWEzLNFg4V2xAsDN9uC4="
+ }
+ }
+ },
+ "extract-zip": {
+ "version": "1.6.6",
+ "resolved": "http://registry.npm.taobao.org/extract-zip/download/extract-zip-1.6.6.tgz",
+ "integrity": "sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw=",
+ "requires": {
+ "concat-stream": "1.6.0",
+ "debug": "2.6.9",
+ "mkdirp": "0.5.0",
+ "yauzl": "2.4.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz",
+ "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "http://registry.npm.taobao.org/extsprintf/download/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
+ },
+ "fast-deep-equal": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-1.1.0.tgz",
+ "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/fast-json-stable-stringify/download/fast-json-stable-stringify-2.0.0.tgz",
+ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "http://registry.npm.taobao.org/fast-levenshtein/download/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "fd-slicer": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/fd-slicer/download/fd-slicer-1.0.1.tgz",
+ "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
+ "requires": {
+ "pend": "~1.2.0"
+ }
+ },
+ "finalhandler": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npm.taobao.org/finalhandler/download/finalhandler-1.1.1.tgz",
+ "integrity": "sha1-7r9O2EAHnIP0JJA4ydcDAIMBsQU=",
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.2",
+ "statuses": "~1.4.0",
+ "unpipe": "~1.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz",
+ "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "http://registry.npm.taobao.org/find-up/download/find-up-3.0.0.tgz",
+ "integrity": "sha1-SRafHXmTQwZG2mHsxa41XCHJe3M=",
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "follow-redirects": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
+ "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
+ "requires": {
+ "debug": "=3.1.0"
+ }
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "http://registry.npm.taobao.org/forever-agent/download/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+ },
+ "form-data": {
+ "version": "2.3.2",
+ "resolved": "http://registry.npm.taobao.org/form-data/download/form-data-2.3.2.tgz",
+ "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "formidable": {
+ "version": "1.2.1",
+ "resolved": "http://registry.npm.taobao.org/formidable/download/formidable-1.2.1.tgz",
+ "integrity": "sha1-cPt8oCkO5v+WEJBBX0s989IIJlk="
+ },
+ "formstream": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npm.taobao.org/formstream/download/formstream-1.1.0.tgz",
+ "integrity": "sha1-UfOXDyYTbrCtRDBN5M67UCB7RHk=",
+ "requires": {
+ "destroy": "^1.0.4",
+ "mime": "^1.3.4",
+ "pause-stream": "~0.0.11"
+ },
+ "dependencies": {
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "http://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz",
+ "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE="
+ }
+ }
+ },
+ "forwarded": {
+ "version": "0.1.2",
+ "resolved": "http://registry.npm.taobao.org/forwarded/download/forwarded-0.1.2.tgz",
+ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
+ },
+ "frameguard": {
+ "version": "3.0.0",
+ "resolved": "http://registry.npm.taobao.org/frameguard/download/frameguard-3.0.0.tgz",
+ "integrity": "sha1-e8rUae57lukdEs6zlZx4I1qScuk="
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "http://registry.npm.taobao.org/fresh/download/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+ },
+ "fs-extra": {
+ "version": "7.0.0",
+ "resolved": "http://registry.npm.taobao.org/fs-extra/download/fs-extra-7.0.0.tgz",
+ "integrity": "sha1-jMP0fOB+97NZOhG5+yRffjTAQdY=",
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz",
+ "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0="
+ },
+ "generate-function": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/generate-function/download/generate-function-2.0.0.tgz",
+ "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ="
+ },
+ "generic-pool": {
+ "version": "3.4.2",
+ "resolved": "http://registry.npm.taobao.org/generic-pool/download/generic-pool-3.4.2.tgz",
+ "integrity": "sha1-kv9xllINZwg5pnMICSoSqt8valk="
+ },
+ "get-caller-file": {
+ "version": "1.0.3",
+ "resolved": "http://registry.npm.taobao.org/get-caller-file/download/get-caller-file-1.0.3.tgz",
+ "integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o="
+ },
+ "get-stream": {
+ "version": "3.0.0",
+ "resolved": "http://registry.npm.taobao.org/get-stream/download/get-stream-3.0.0.tgz",
+ "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "http://registry.npm.taobao.org/getpass/download/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "glob": {
+ "version": "5.0.15",
+ "resolved": "http://registry.npm.taobao.org/glob/download/glob-5.0.15.tgz",
+ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.1.11",
+ "resolved": "http://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.1.11.tgz",
+ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+ },
+ "graceful-readlink": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/graceful-readlink/download/graceful-readlink-1.0.1.tgz",
+ "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU="
+ },
+ "growl": {
+ "version": "1.9.2",
+ "resolved": "http://registry.npm.taobao.org/growl/download/growl-1.9.2.tgz",
+ "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=",
+ "dev": true
+ },
+ "handlebars": {
+ "version": "4.0.11",
+ "resolved": "http://registry.npm.taobao.org/handlebars/download/handlebars-4.0.11.tgz",
+ "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=",
+ "dev": true,
+ "requires": {
+ "async": "^1.4.0",
+ "optimist": "^0.6.1",
+ "source-map": "^0.4.4",
+ "uglify-js": "^2.6"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.4.4",
+ "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.4.4.tgz",
+ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+ "dev": true,
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ }
+ }
+ },
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/har-schema/download/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
+ },
+ "har-validator": {
+ "version": "5.0.3",
+ "resolved": "http://registry.npm.taobao.org/har-validator/download/har-validator-5.0.3.tgz",
+ "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
+ "requires": {
+ "ajv": "^5.1.0",
+ "har-schema": "^2.0.0"
+ }
+ },
+ "has": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/has/download/has-1.0.1.tgz",
+ "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=",
+ "requires": {
+ "function-bind": "^1.0.2"
+ }
+ },
+ "has-flag": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz",
+ "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
+ "dev": true
+ },
+ "hawk": {
+ "version": "6.0.2",
+ "resolved": "http://registry.npm.taobao.org/hawk/download/hawk-6.0.2.tgz",
+ "integrity": "sha1-r02RTrBl+bXOTZ0RwcshJu7MMDg=",
+ "requires": {
+ "boom": "4.x.x",
+ "cryptiles": "3.x.x",
+ "hoek": "4.x.x",
+ "sntp": "2.x.x"
+ }
+ },
+ "he": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npm.taobao.org/he/download/he-1.1.1.tgz",
+ "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
+ "dev": true
+ },
+ "helmet": {
+ "version": "3.12.0",
+ "resolved": "http://registry.npm.taobao.org/helmet/download/helmet-3.12.0.tgz",
+ "integrity": "sha1-IJjjXPTlHGTC8dOGcLfTgqN32Sw=",
+ "requires": {
+ "dns-prefetch-control": "0.1.0",
+ "dont-sniff-mimetype": "1.0.0",
+ "expect-ct": "0.1.0",
+ "frameguard": "3.0.0",
+ "helmet-csp": "2.7.0",
+ "hide-powered-by": "1.0.0",
+ "hpkp": "2.0.0",
+ "hsts": "2.1.0",
+ "ienoopen": "1.0.0",
+ "nocache": "2.0.0",
+ "referrer-policy": "1.1.0",
+ "x-xss-protection": "1.1.0"
+ }
+ },
+ "helmet-csp": {
+ "version": "2.7.0",
+ "resolved": "http://registry.npm.taobao.org/helmet-csp/download/helmet-csp-2.7.0.tgz",
+ "integrity": "sha1-eTQJRhfR/re7LcQ7t9nogw93RxY=",
+ "requires": {
+ "camelize": "1.0.0",
+ "content-security-policy-builder": "2.0.0",
+ "dasherize": "2.0.0",
+ "lodash.reduce": "4.6.0",
+ "platform": "1.3.5"
+ }
+ },
+ "hide-powered-by": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/hide-powered-by/download/hide-powered-by-1.0.0.tgz",
+ "integrity": "sha1-SoWtZYgfYoV/xwr3F0oRhNzM4ys="
+ },
+ "hmacsha1": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/hmacsha1/-/hmacsha1-1.0.0.tgz",
+ "integrity": "sha1-wbeuA6TqEWNICQrxT4FIwSk4qRc="
+ },
+ "hoek": {
+ "version": "4.2.1",
+ "resolved": "http://registry.npm.taobao.org/hoek/download/hoek-4.2.1.tgz",
+ "integrity": "sha1-ljRQKqEsRF3Vp8VzS1cruHOKrLs="
+ },
+ "hpkp": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/hpkp/download/hpkp-2.0.0.tgz",
+ "integrity": "sha1-EOFCJk52IVpdMMROxD3mTe5tFnI="
+ },
+ "hsts": {
+ "version": "2.1.0",
+ "resolved": "http://registry.npm.taobao.org/hsts/download/hsts-2.1.0.tgz",
+ "integrity": "sha1-y9bJGKI4X+4d1WgL+ys6GUwBIcw="
+ },
+ "http-errors": {
+ "version": "1.6.2",
+ "resolved": "http://registry.npm.taobao.org/http-errors/download/http-errors-1.6.2.tgz",
+ "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
+ "requires": {
+ "depd": "1.1.1",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.0.3",
+ "statuses": ">= 1.3.1 < 2"
+ },
+ "dependencies": {
+ "depd": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npm.taobao.org/depd/download/depd-1.1.1.tgz",
+ "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
+ }
+ }
+ },
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npm.taobao.org/http-signature/download/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
+ "humanize-ms": {
+ "version": "1.2.1",
+ "resolved": "http://registry.npm.taobao.org/humanize-ms/download/humanize-ms-1.2.1.tgz",
+ "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
+ "requires": {
+ "ms": "^2.0.0"
+ }
+ },
+ "i18n": {
+ "version": "0.8.3",
+ "resolved": "http://registry.npm.taobao.org/i18n/download/i18n-0.8.3.tgz",
+ "integrity": "sha1-LYzxwkciYCwgQdAbpq5eqlE4jw4=",
+ "requires": {
+ "debug": "*",
+ "make-plural": "^3.0.3",
+ "math-interval-parser": "^1.1.0",
+ "messageformat": "^0.3.1",
+ "mustache": "*",
+ "sprintf-js": ">=1.0.3"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.19",
+ "resolved": "http://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.19.tgz",
+ "integrity": "sha1-90aPYBNfXl2tM5nAqBvpoWA6CCs="
+ },
+ "ieee754": {
+ "version": "1.1.10",
+ "resolved": "http://registry.npm.taobao.org/ieee754/download/ieee754-1.1.10.tgz",
+ "integrity": "sha1-cZpvewJoMeZL24OLDeG7ACm79xY="
+ },
+ "ienoopen": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/ienoopen/download/ienoopen-1.0.0.tgz",
+ "integrity": "sha1-NGpCj0dKrI9QzzeE6i0PFvYr2ms="
+ },
+ "ignorefs": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npm.taobao.org/ignorefs/download/ignorefs-1.2.0.tgz",
+ "integrity": "sha1-2ln7hYl25KXkNwLM0fKC/byeV1Y=",
+ "requires": {
+ "editions": "^1.3.3",
+ "ignorepatterns": "^1.1.0"
+ }
+ },
+ "ignorepatterns": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npm.taobao.org/ignorepatterns/download/ignorepatterns-1.1.0.tgz",
+ "integrity": "sha1-rI9DbyI5td+2bV8NOpBKh6xnzF4="
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "http://registry.npm.taobao.org/inflight/download/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "http://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "invert-kv": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/invert-kv/download/invert-kv-1.0.0.tgz",
+ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
+ },
+ "ipaddr.js": {
+ "version": "1.6.0",
+ "resolved": "http://registry.npm.taobao.org/ipaddr.js/download/ipaddr.js-1.6.0.tgz",
+ "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs="
+ },
+ "is-bluebird": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npm.taobao.org/is-bluebird/download/is-bluebird-1.0.2.tgz",
+ "integrity": "sha1-CWQ5Bg9KpBGr7hkUOoTWpVNG1uI="
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "http://registry.npm.taobao.org/is-buffer/download/is-buffer-1.1.6.tgz",
+ "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4="
+ },
+ "is-expression": {
+ "version": "3.0.0",
+ "resolved": "http://registry.npm.taobao.org/is-expression/download/is-expression-3.0.0.tgz",
+ "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=",
+ "requires": {
+ "acorn": "~4.0.2",
+ "object-assign": "^4.0.1"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "4.0.13",
+ "resolved": "http://registry.npm.taobao.org/acorn/download/acorn-4.0.13.tgz",
+ "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="
+ }
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "is-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
+ },
+ "is-promise": {
+ "version": "2.1.0",
+ "resolved": "http://registry.npm.taobao.org/is-promise/download/is-promise-2.1.0.tgz",
+ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o="
+ },
+ "is-regex": {
+ "version": "1.0.4",
+ "resolved": "http://registry.npm.taobao.org/is-regex/download/is-regex-1.0.4.tgz",
+ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
+ "requires": {
+ "has": "^1.0.1"
+ }
+ },
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npm.taobao.org/is-stream/download/is-stream-1.1.0.tgz",
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/is-typedarray/download/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/isexe/download/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "http://registry.npm.taobao.org/isstream/download/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+ },
+ "istanbul": {
+ "version": "0.4.5",
+ "resolved": "http://registry.npm.taobao.org/istanbul/download/istanbul-0.4.5.tgz",
+ "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=",
+ "dev": true,
+ "requires": {
+ "abbrev": "1.0.x",
+ "async": "1.x",
+ "escodegen": "1.8.x",
+ "esprima": "2.7.x",
+ "glob": "^5.0.15",
+ "handlebars": "^4.0.1",
+ "js-yaml": "3.x",
+ "mkdirp": "0.5.x",
+ "nopt": "3.x",
+ "once": "1.x",
+ "resolve": "1.1.x",
+ "supports-color": "^3.1.0",
+ "which": "^1.1.1",
+ "wordwrap": "^1.0.0"
+ },
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.9",
+ "resolved": "http://registry.npm.taobao.org/abbrev/download/abbrev-1.0.9.tgz",
+ "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=",
+ "dev": true
+ },
+ "escodegen": {
+ "version": "1.8.1",
+ "resolved": "http://registry.npm.taobao.org/escodegen/download/escodegen-1.8.1.tgz",
+ "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=",
+ "dev": true,
+ "requires": {
+ "esprima": "^2.7.1",
+ "estraverse": "^1.9.1",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1",
+ "source-map": "~0.2.0"
+ }
+ },
+ "esprima": {
+ "version": "2.7.3",
+ "resolved": "http://registry.npm.taobao.org/esprima/download/esprima-2.7.3.tgz",
+ "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
+ "dev": true
+ },
+ "estraverse": {
+ "version": "1.9.3",
+ "resolved": "http://registry.npm.taobao.org/estraverse/download/estraverse-1.9.3.tgz",
+ "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "http://registry.npm.taobao.org/resolve/download/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.2.0",
+ "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.2.0.tgz",
+ "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ },
+ "supports-color": {
+ "version": "3.2.3",
+ "resolved": "http://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz",
+ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
+ "dev": true,
+ "requires": {
+ "has-flag": "^1.0.0"
+ }
+ }
+ }
+ },
+ "jmespath": {
+ "version": "0.15.0",
+ "resolved": "http://registry.npm.taobao.org/jmespath/download/jmespath-0.15.0.tgz",
+ "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
+ },
+ "js-stringify": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npm.taobao.org/js-stringify/download/js-stringify-1.0.2.tgz",
+ "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds="
+ },
+ "js-yaml": {
+ "version": "3.11.0",
+ "resolved": "http://registry.npm.taobao.org/js-yaml/download/js-yaml-3.11.0.tgz",
+ "integrity": "sha1-WXwai9VxUvJtYizkEXhRpR9euu8=",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "dependencies": {
+ "esprima": {
+ "version": "4.0.0",
+ "resolved": "http://registry.npm.taobao.org/esprima/download/esprima-4.0.0.tgz",
+ "integrity": "sha1-RJnt3NERDgshi6zy+n9/WfVcqAQ=",
+ "dev": true
+ }
+ }
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "http://registry.npm.taobao.org/jsbn/download/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+ "optional": true
+ },
+ "jschardet": {
+ "version": "1.6.0",
+ "resolved": "http://registry.npm.taobao.org/jschardet/download/jschardet-1.6.0.tgz",
+ "integrity": "sha1-x9GnHtz/KDnbL57DD8XV69PBpng="
+ },
+ "json-schema": {
+ "version": "0.2.3",
+ "resolved": "http://registry.npm.taobao.org/json-schema/download/json-schema-0.2.3.tgz",
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
+ },
+ "json-schema-traverse": {
+ "version": "0.3.1",
+ "resolved": "http://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.3.1.tgz",
+ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "http://registry.npm.taobao.org/json-stringify-safe/download/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+ },
+ "json3": {
+ "version": "3.3.2",
+ "resolved": "http://registry.npm.taobao.org/json3/download/json3-3.3.2.tgz",
+ "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=",
+ "dev": true
+ },
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved": "http://registry.npm.taobao.org/jsonfile/download/jsonfile-4.0.0.tgz",
+ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "jsonwebtoken": {
+ "version": "8.2.0",
+ "resolved": "http://registry.npm.taobao.org/jsonwebtoken/download/jsonwebtoken-8.2.0.tgz",
+ "integrity": "sha1-aQ7DqefpXiiENHzj6eudOJqlmLM=",
+ "requires": {
+ "jws": "^3.1.4",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "xtend": "^4.0.1"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "http://registry.npm.taobao.org/ms/download/ms-2.1.1.tgz",
+ "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo="
+ }
+ }
+ },
+ "jsprim": {
+ "version": "1.4.1",
+ "resolved": "http://registry.npm.taobao.org/jsprim/download/jsprim-1.4.1.tgz",
+ "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.2.3",
+ "verror": "1.10.0"
+ }
+ },
+ "jstransformer": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/jstransformer/download/jstransformer-1.0.0.tgz",
+ "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=",
+ "requires": {
+ "is-promise": "^2.0.0",
+ "promise": "^7.0.1"
+ }
+ },
+ "jwa": {
+ "version": "1.1.5",
+ "resolved": "http://registry.npm.taobao.org/jwa/download/jwa-1.1.5.tgz",
+ "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=",
+ "requires": {
+ "base64url": "2.0.0",
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.9",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "3.1.4",
+ "resolved": "http://registry.npm.taobao.org/jws/download/jws-3.1.4.tgz",
+ "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=",
+ "requires": {
+ "base64url": "^2.0.0",
+ "jwa": "^1.1.4",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "http://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ },
+ "lazy-cache": {
+ "version": "1.0.4",
+ "resolved": "http://registry.npm.taobao.org/lazy-cache/download/lazy-cache-1.0.4.tgz",
+ "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4="
+ },
+ "lcid": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/lcid/download/lcid-1.0.0.tgz",
+ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+ "requires": {
+ "invert-kv": "^1.0.0"
+ }
+ },
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "http://registry.npm.taobao.org/levn/download/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ }
+ },
+ "linkify-it": {
+ "version": "2.0.3",
+ "resolved": "http://registry.npm.taobao.org/linkify-it/download/linkify-it-2.0.3.tgz",
+ "integrity": "sha1-2UpGSPmxwXnWT6lykSaL22zpQ08=",
+ "requires": {
+ "uc.micro": "^1.0.1"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "http://registry.npm.taobao.org/locate-path/download/locate-path-3.0.0.tgz",
+ "integrity": "sha1-2+w7OrdZdYBxtY/ln8QYca8hQA4=",
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.5",
+ "resolved": "http://registry.npm.taobao.org/lodash/download/lodash-4.17.5.tgz",
+ "integrity": "sha1-maktZcAnLevoyWtgV7yPv6O+1RE="
+ },
+ "lodash._baseassign": {
+ "version": "3.2.0",
+ "resolved": "http://registry.npm.taobao.org/lodash._baseassign/download/lodash._baseassign-3.2.0.tgz",
+ "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=",
+ "dev": true,
+ "requires": {
+ "lodash._basecopy": "^3.0.0",
+ "lodash.keys": "^3.0.0"
+ }
+ },
+ "lodash._basecopy": {
+ "version": "3.0.1",
+ "resolved": "http://registry.npm.taobao.org/lodash._basecopy/download/lodash._basecopy-3.0.1.tgz",
+ "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=",
+ "dev": true
+ },
+ "lodash._basecreate": {
+ "version": "3.0.3",
+ "resolved": "http://registry.npm.taobao.org/lodash._basecreate/download/lodash._basecreate-3.0.3.tgz",
+ "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=",
+ "dev": true
+ },
+ "lodash._getnative": {
+ "version": "3.9.1",
+ "resolved": "http://registry.npm.taobao.org/lodash._getnative/download/lodash._getnative-3.9.1.tgz",
+ "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=",
+ "dev": true
+ },
+ "lodash._isiterateecall": {
+ "version": "3.0.9",
+ "resolved": "http://registry.npm.taobao.org/lodash._isiterateecall/download/lodash._isiterateecall-3.0.9.tgz",
+ "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=",
+ "dev": true
+ },
+ "lodash.create": {
+ "version": "3.1.1",
+ "resolved": "http://registry.npm.taobao.org/lodash.create/download/lodash.create-3.1.1.tgz",
+ "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=",
+ "dev": true,
+ "requires": {
+ "lodash._baseassign": "^3.0.0",
+ "lodash._basecreate": "^3.0.0",
+ "lodash._isiterateecall": "^3.0.0"
+ }
+ },
+ "lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "http://registry.npm.taobao.org/lodash.includes/download/lodash.includes-4.3.0.tgz",
+ "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
+ },
+ "lodash.isarguments": {
+ "version": "3.1.0",
+ "resolved": "http://registry.npm.taobao.org/lodash.isarguments/download/lodash.isarguments-3.1.0.tgz",
+ "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=",
+ "dev": true
+ },
+ "lodash.isarray": {
+ "version": "3.0.4",
+ "resolved": "http://registry.npm.taobao.org/lodash.isarray/download/lodash.isarray-3.0.4.tgz",
+ "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
+ "dev": true
+ },
+ "lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "http://registry.npm.taobao.org/lodash.isboolean/download/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
+ },
+ "lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "http://registry.npm.taobao.org/lodash.isinteger/download/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
+ },
+ "lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "http://registry.npm.taobao.org/lodash.isnumber/download/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
+ },
+ "lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "http://registry.npm.taobao.org/lodash.isplainobject/download/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+ },
+ "lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "http://registry.npm.taobao.org/lodash.isstring/download/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
+ },
+ "lodash.keys": {
+ "version": "3.1.2",
+ "resolved": "http://registry.npm.taobao.org/lodash.keys/download/lodash.keys-3.1.2.tgz",
+ "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
+ "dev": true,
+ "requires": {
+ "lodash._getnative": "^3.0.0",
+ "lodash.isarguments": "^3.0.0",
+ "lodash.isarray": "^3.0.0"
+ }
+ },
+ "lodash.once": {
+ "version": "4.1.1",
+ "resolved": "http://registry.npm.taobao.org/lodash.once/download/lodash.once-4.1.1.tgz",
+ "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
+ },
+ "lodash.reduce": {
+ "version": "4.6.0",
+ "resolved": "http://registry.npm.taobao.org/lodash.reduce/download/lodash.reduce-4.6.0.tgz",
+ "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs="
+ },
+ "log4js": {
+ "version": "3.0.5",
+ "resolved": "http://registry.npm.taobao.org/log4js/download/log4js-3.0.5.tgz",
+ "integrity": "sha1-uAFGv+utaLQw1PNWlVbYpu3+8wM=",
+ "requires": {
+ "circular-json": "^0.5.5",
+ "date-format": "^1.2.0",
+ "debug": "^3.1.0",
+ "rfdc": "^1.1.2",
+ "streamroller": "0.7.0"
+ }
+ },
+ "long": {
+ "version": "2.4.0",
+ "resolved": "http://registry.npm.taobao.org/long/download/long-2.4.0.tgz",
+ "integrity": "sha1-n6GAux2VAM3CnEFWdmoZleH0Uk8="
+ },
+ "longest": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/longest/download/longest-1.0.1.tgz",
+ "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
+ },
+ "lru-cache": {
+ "version": "4.1.3",
+ "resolved": "http://registry.npm.taobao.org/lru-cache/download/lru-cache-4.1.3.tgz",
+ "integrity": "sha1-oRdc80lt/IQ2wVbDNLSVWZK85pw=",
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "make-dir": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
+ "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
+ "requires": {
+ "pify": "^3.0.0"
+ }
+ },
+ "make-plural": {
+ "version": "3.0.6",
+ "resolved": "http://registry.npm.taobao.org/make-plural/download/make-plural-3.0.6.tgz",
+ "integrity": "sha1-IDOgO6wpC487uRJY9lud9+iwHKc=",
+ "requires": {
+ "minimist": "^1.2.0"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npm.taobao.org/minimist/download/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "optional": true
+ }
+ }
+ },
+ "markdown-it": {
+ "version": "8.4.1",
+ "resolved": "http://registry.npm.taobao.org/markdown-it/download/markdown-it-8.4.1.tgz",
+ "integrity": "sha1-IG/lmw5OG3inxzJQr5s0pK0Kr0Q=",
+ "requires": {
+ "argparse": "^1.0.7",
+ "entities": "~1.1.1",
+ "linkify-it": "^2.0.0",
+ "mdurl": "^1.0.1",
+ "uc.micro": "^1.0.5"
+ }
+ },
+ "math-interval-parser": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npm.taobao.org/math-interval-parser/download/math-interval-parser-1.1.0.tgz",
+ "integrity": "sha1-2+2lsGsySZc8bfYXD94jhvCv2JM=",
+ "requires": {
+ "xregexp": "^2.0.0"
+ }
+ },
+ "md5": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
+ "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=",
+ "requires": {
+ "charenc": "~0.0.1",
+ "crypt": "~0.0.1",
+ "is-buffer": "~1.1.1"
+ }
+ },
+ "mdurl": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/mdurl/download/mdurl-1.0.1.tgz",
+ "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "http://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+ },
+ "mem": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npm.taobao.org/mem/download/mem-1.1.0.tgz",
+ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=",
+ "requires": {
+ "mimic-fn": "^1.0.0"
+ }
+ },
+ "merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/merge-descriptors/download/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+ },
+ "messageformat": {
+ "version": "0.3.1",
+ "resolved": "http://registry.npm.taobao.org/messageformat/download/messageformat-0.3.1.tgz",
+ "integrity": "sha1-5Y//gkXps5cXmeW0PbWLPpQX9aI=",
+ "requires": {
+ "async": "~1.5.2",
+ "glob": "~6.0.4",
+ "make-plural": "~3.0.3",
+ "nopt": "~3.0.6",
+ "watchr": "~2.4.13"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "6.0.4",
+ "resolved": "http://registry.npm.taobao.org/glob/download/glob-6.0.4.tgz",
+ "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=",
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "methods": {
+ "version": "1.1.2",
+ "resolved": "http://registry.npm.taobao.org/methods/download/methods-1.1.2.tgz",
+ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+ },
+ "mime-db": {
+ "version": "1.33.0",
+ "resolved": "http://registry.npm.taobao.org/mime-db/download/mime-db-1.33.0.tgz",
+ "integrity": "sha1-o0kgUKXLm2NFBUHjnZeI0icng9s="
+ },
+ "mime-types": {
+ "version": "2.1.18",
+ "resolved": "http://registry.npm.taobao.org/mime-types/download/mime-types-2.1.18.tgz",
+ "integrity": "sha1-bzI/YKg9ERRvgx/xH9ZuL+VQO7g=",
+ "requires": {
+ "mime-db": "~1.33.0"
+ }
+ },
+ "mimic-fn": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npm.taobao.org/mimic-fn/download/mimic-fn-1.2.0.tgz",
+ "integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI="
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "http://registry.npm.taobao.org/minimatch/download/minimatch-3.0.4.tgz",
+ "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "http://registry.npm.taobao.org/minimist/download/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ },
+ "mkdirp": {
+ "version": "0.5.0",
+ "resolved": "http://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.0.tgz",
+ "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=",
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "mocha": {
+ "version": "3.5.3",
+ "resolved": "http://registry.npm.taobao.org/mocha/download/mocha-3.5.3.tgz",
+ "integrity": "sha1-HgSA/jbS2lhY0etqzDhBiybqog0=",
+ "dev": true,
+ "requires": {
+ "browser-stdout": "1.3.0",
+ "commander": "2.9.0",
+ "debug": "2.6.8",
+ "diff": "3.2.0",
+ "escape-string-regexp": "1.0.5",
+ "glob": "7.1.1",
+ "growl": "1.9.2",
+ "he": "1.1.1",
+ "json3": "3.3.2",
+ "lodash.create": "3.1.1",
+ "mkdirp": "0.5.1",
+ "supports-color": "3.1.2"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.9.0",
+ "resolved": "http://registry.npm.taobao.org/commander/download/commander-2.9.0.tgz",
+ "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
+ "dev": true,
+ "requires": {
+ "graceful-readlink": ">= 1.0.0"
+ }
+ },
+ "debug": {
+ "version": "2.6.8",
+ "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.8.tgz",
+ "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.1",
+ "resolved": "http://registry.npm.taobao.org/glob/download/glob-7.1.1.tgz",
+ "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.2",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "http://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "supports-color": {
+ "version": "3.1.2",
+ "resolved": "http://registry.npm.taobao.org/supports-color/download/supports-color-3.1.2.tgz",
+ "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=",
+ "dev": true,
+ "requires": {
+ "has-flag": "^1.0.0"
+ }
+ }
+ }
+ },
+ "moment": {
+ "version": "2.21.0",
+ "resolved": "http://registry.npm.taobao.org/moment/download/moment-2.21.0.tgz",
+ "integrity": "sha1-KhFLUdKm7J5tg8+AP4OKh42KAjo="
+ },
+ "moment-timezone": {
+ "version": "0.5.14",
+ "resolved": "http://registry.npm.taobao.org/moment-timezone/download/moment-timezone-0.5.14.tgz",
+ "integrity": "sha1-TrOP+VOLgBCLpGekWPPtQmjM/LE=",
+ "requires": {
+ "moment": ">= 2.9.0"
+ }
+ },
+ "morgan": {
+ "version": "1.9.0",
+ "resolved": "http://registry.npm.taobao.org/morgan/download/morgan-1.9.0.tgz",
+ "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=",
+ "requires": {
+ "basic-auth": "~2.0.0",
+ "debug": "2.6.9",
+ "depd": "~1.1.1",
+ "on-finished": "~2.3.0",
+ "on-headers": "~1.0.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz",
+ "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ },
+ "mustache": {
+ "version": "2.3.0",
+ "resolved": "http://registry.npm.taobao.org/mustache/download/mustache-2.3.0.tgz",
+ "integrity": "sha1-QCj3d4sXcIpImTCm5SrDvKDaQdA="
+ },
+ "mysql2": {
+ "version": "1.5.2",
+ "resolved": "http://registry.npm.taobao.org/mysql2/download/mysql2-1.5.2.tgz",
+ "integrity": "sha1-5OBzg5uxCXJmmyQOx3Bh1/9Vz1k=",
+ "requires": {
+ "cardinal": "1.0.0",
+ "denque": "^1.1.1",
+ "generate-function": "^2.0.0",
+ "iconv-lite": "^0.4.18",
+ "long": "^4.0.0",
+ "lru-cache": "^4.1.1",
+ "named-placeholders": "1.1.1",
+ "object-assign": "^4.1.1",
+ "readable-stream": "2.3.2",
+ "safe-buffer": "^5.0.1",
+ "seq-queue": "0.0.5",
+ "sqlstring": "^2.2.0"
+ },
+ "dependencies": {
+ "long": {
+ "version": "4.0.0",
+ "resolved": "http://registry.npm.taobao.org/long/download/long-4.0.0.tgz",
+ "integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg="
+ },
+ "lru-cache": {
+ "version": "4.1.2",
+ "resolved": "http://registry.npm.taobao.org/lru-cache/download/lru-cache-4.1.2.tgz",
+ "integrity": "sha1-RSNLLm4vKzPaElYkxGZJKaAiTD8=",
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "process-nextick-args": {
+ "version": "1.0.7",
+ "resolved": "http://registry.npm.taobao.org/process-nextick-args/download/process-nextick-args-1.0.7.tgz",
+ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
+ },
+ "readable-stream": {
+ "version": "2.3.2",
+ "resolved": "http://registry.npm.taobao.org/readable-stream/download/readable-stream-2.3.2.tgz",
+ "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~1.0.6",
+ "safe-buffer": "~5.1.0",
+ "string_decoder": "~1.0.0",
+ "util-deprecate": "~1.0.1"
+ }
+ }
+ }
+ },
+ "named-placeholders": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npm.taobao.org/named-placeholders/download/named-placeholders-1.1.1.tgz",
+ "integrity": "sha1-O3oNJiA910s6nfTJz7gnsvuQfmQ=",
+ "requires": {
+ "lru-cache": "2.5.0"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0",
+ "resolved": "http://registry.npm.taobao.org/lru-cache/download/lru-cache-2.5.0.tgz",
+ "integrity": "sha1-2COIrpyWC+y+oMc7uet5tsbOmus="
+ }
+ }
+ },
+ "negotiator": {
+ "version": "0.6.1",
+ "resolved": "http://registry.npm.taobao.org/negotiator/download/negotiator-0.6.1.tgz",
+ "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
+ },
+ "nocache": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/nocache/download/nocache-2.0.0.tgz",
+ "integrity": "sha1-ICtIAhoMTL3i34DeFaF0Q8i0OYA="
+ },
+ "node_memcached": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npm.taobao.org/node_memcached/download/node_memcached-1.1.3.tgz",
+ "integrity": "sha1-icFSr4itKIF/ANiRyZBFHV1xLqg=",
+ "requires": {
+ "debug": "^2.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz",
+ "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "nodemailer": {
+ "version": "4.6.3",
+ "resolved": "http://registry.npm.taobao.org/nodemailer/download/nodemailer-4.6.3.tgz",
+ "integrity": "sha1-w7fpf7cvRtSkdcQGoV7SOkXbzdw="
+ },
+ "nopt": {
+ "version": "3.0.6",
+ "resolved": "http://registry.npm.taobao.org/nopt/download/nopt-3.0.6.tgz",
+ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "npm-run-path": {
+ "version": "2.0.2",
+ "resolved": "http://registry.npm.taobao.org/npm-run-path/download/npm-run-path-2.0.2.tgz",
+ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+ "requires": {
+ "path-key": "^2.0.0"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/number-is-nan/download/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+ },
+ "oauth-sign": {
+ "version": "0.8.2",
+ "resolved": "http://registry.npm.taobao.org/oauth-sign/download/oauth-sign-0.8.2.tgz",
+ "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "http://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "http://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "on-headers": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/on-headers/download/on-headers-1.0.1.tgz",
+ "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c="
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "http://registry.npm.taobao.org/once/download/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "resolved": "http://registry.npm.taobao.org/optimist/download/optimist-0.6.1.tgz",
+ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+ "dev": true,
+ "requires": {
+ "minimist": "~0.0.1",
+ "wordwrap": "~0.0.2"
+ },
+ "dependencies": {
+ "wordwrap": {
+ "version": "0.0.3",
+ "resolved": "http://registry.npm.taobao.org/wordwrap/download/wordwrap-0.0.3.tgz",
+ "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
+ "dev": true
+ }
+ }
+ },
+ "optionator": {
+ "version": "0.8.2",
+ "resolved": "http://registry.npm.taobao.org/optionator/download/optionator-0.8.2.tgz",
+ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.4",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "wordwrap": "~1.0.0"
+ }
+ },
+ "optjs": {
+ "version": "3.2.2",
+ "resolved": "http://registry.npm.taobao.org/optjs/download/optjs-3.2.2.tgz",
+ "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4="
+ },
+ "os-locale": {
+ "version": "1.4.0",
+ "resolved": "http://registry.npm.taobao.org/os-locale/download/os-locale-1.4.0.tgz",
+ "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
+ "requires": {
+ "lcid": "^1.0.0"
+ }
+ },
+ "os-name": {
+ "version": "1.0.3",
+ "resolved": "http://registry.npm.taobao.org/os-name/download/os-name-1.0.3.tgz",
+ "integrity": "sha1-GzefZINa98Wn9JizV8uVIVwVnt8=",
+ "requires": {
+ "osx-release": "^1.0.0",
+ "win-release": "^1.0.0"
+ }
+ },
+ "osx-release": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npm.taobao.org/osx-release/download/osx-release-1.1.0.tgz",
+ "integrity": "sha1-8heRGigTaUmvG/kwiyQeJzfTzWw=",
+ "requires": {
+ "minimist": "^1.1.0"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npm.taobao.org/minimist/download/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+ }
+ }
+ },
+ "p-finally": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/p-finally/download/p-finally-1.0.0.tgz",
+ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
+ },
+ "p-limit": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/p-limit/download/p-limit-2.0.0.tgz",
+ "integrity": "sha1-5iTtVO6MRgp3izyfNnBJb/ileuw=",
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "http://registry.npm.taobao.org/p-locate/download/p-locate-3.0.0.tgz",
+ "integrity": "sha1-Mi1poFwCZLJZl9n0DNiokasAZKQ=",
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "p-try": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/p-try/download/p-try-2.0.0.tgz",
+ "integrity": "sha1-hQgLuHxkaI+keZb+j3376CEXYLE="
+ },
+ "parseurl": {
+ "version": "1.3.2",
+ "resolved": "http://registry.npm.taobao.org/parseurl/download/parseurl-1.3.2.tgz",
+ "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "http://registry.npm.taobao.org/path-exists/download/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "http://registry.npm.taobao.org/path-key/download/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
+ },
+ "path-parse": {
+ "version": "1.0.5",
+ "resolved": "http://registry.npm.taobao.org/path-parse/download/path-parse-1.0.5.tgz",
+ "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME="
+ },
+ "path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "http://registry.npm.taobao.org/path-to-regexp/download/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+ },
+ "pause-stream": {
+ "version": "0.0.11",
+ "resolved": "http://registry.npm.taobao.org/pause-stream/download/pause-stream-0.0.11.tgz",
+ "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=",
+ "requires": {
+ "through": "~2.3"
+ }
+ },
+ "pend": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npm.taobao.org/pend/download/pend-1.2.0.tgz",
+ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "http://registry.npm.taobao.org/performance-now/download/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+ },
+ "pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
+ },
+ "platform": {
+ "version": "1.3.5",
+ "resolved": "http://registry.npm.taobao.org/platform/download/platform-1.3.5.tgz",
+ "integrity": "sha1-+2lYxpbgfikY0u7aDwvJRI1zNEQ="
+ },
+ "pomelo-protobuf": {
+ "version": "0.4.0",
+ "resolved": "http://registry.npm.taobao.org/pomelo-protobuf/download/pomelo-protobuf-0.4.0.tgz",
+ "integrity": "sha1-5F6aCkRusYZn4MbhPutT1Hrdvag="
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "http://registry.npm.taobao.org/prelude-ls/download/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/process-nextick-args/download/process-nextick-args-2.0.0.tgz",
+ "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o="
+ },
+ "promise": {
+ "version": "7.3.1",
+ "resolved": "http://registry.npm.taobao.org/promise/download/promise-7.3.1.tgz",
+ "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=",
+ "requires": {
+ "asap": "~2.0.3"
+ }
+ },
+ "protobufjs": {
+ "version": "4.1.3",
+ "resolved": "http://registry.npm.taobao.org/protobufjs/download/protobufjs-4.1.3.tgz",
+ "integrity": "sha1-jjbRsCJsu2jWR+S0TCoUTzfyd54=",
+ "requires": {
+ "ascli": "~1",
+ "bytebuffer": "~4 >=4.1",
+ "glob": "^5.0.10",
+ "yargs": "^3.10.0"
+ },
+ "dependencies": {
+ "yargs": {
+ "version": "3.32.0",
+ "resolved": "http://registry.npm.taobao.org/yargs/download/yargs-3.32.0.tgz",
+ "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=",
+ "requires": {
+ "camelcase": "^2.0.1",
+ "cliui": "^3.0.3",
+ "decamelize": "^1.1.1",
+ "os-locale": "^1.4.0",
+ "string-width": "^1.0.1",
+ "window-size": "^0.1.4",
+ "y18n": "^3.2.0"
+ }
+ }
+ }
+ },
+ "proxy-addr": {
+ "version": "2.0.3",
+ "resolved": "http://registry.npm.taobao.org/proxy-addr/download/proxy-addr-2.0.3.tgz",
+ "integrity": "sha1-NV8mJQWmIWRrMTCnKOtkfiIFU0E=",
+ "requires": {
+ "forwarded": "~0.1.2",
+ "ipaddr.js": "1.6.0"
+ }
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npm.taobao.org/pseudomap/download/pseudomap-1.0.2.tgz",
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+ },
+ "pug": {
+ "version": "2.0.1",
+ "resolved": "http://registry.npm.taobao.org/pug/download/pug-2.0.1.tgz",
+ "integrity": "sha1-J8FRYStT1ymr6OgoWqxryJNFtdA=",
+ "requires": {
+ "pug-code-gen": "^2.0.1",
+ "pug-filters": "^3.0.1",
+ "pug-lexer": "^4.0.0",
+ "pug-linker": "^3.0.5",
+ "pug-load": "^2.0.11",
+ "pug-parser": "^5.0.0",
+ "pug-runtime": "^2.0.4",
+ "pug-strip-comments": "^1.0.3"
+ }
+ },
+ "pug-attrs": {
+ "version": "2.0.3",
+ "resolved": "http://registry.npm.taobao.org/pug-attrs/download/pug-attrs-2.0.3.tgz",
+ "integrity": "sha1-owlflw5kFR972tlX7vVftdeQXRU=",
+ "requires": {
+ "constantinople": "^3.0.1",
+ "js-stringify": "^1.0.1",
+ "pug-runtime": "^2.0.4"
+ }
+ },
+ "pug-code-gen": {
+ "version": "2.0.1",
+ "resolved": "http://registry.npm.taobao.org/pug-code-gen/download/pug-code-gen-2.0.1.tgz",
+ "integrity": "sha1-CVHsgyJddNjPxHan+Zolm199BQw=",
+ "requires": {
+ "constantinople": "^3.0.1",
+ "doctypes": "^1.1.0",
+ "js-stringify": "^1.0.1",
+ "pug-attrs": "^2.0.3",
+ "pug-error": "^1.3.2",
+ "pug-runtime": "^2.0.4",
+ "void-elements": "^2.0.1",
+ "with": "^5.0.0"
+ }
+ },
+ "pug-error": {
+ "version": "1.3.2",
+ "resolved": "http://registry.npm.taobao.org/pug-error/download/pug-error-1.3.2.tgz",
+ "integrity": "sha1-U659nSm7A89WRJOgJhCfVMR/XyY="
+ },
+ "pug-filters": {
+ "version": "3.0.1",
+ "resolved": "http://registry.npm.taobao.org/pug-filters/download/pug-filters-3.0.1.tgz",
+ "integrity": "sha1-Fj73O/ux8VRNAysrQPRRMOtS3Ms=",
+ "requires": {
+ "clean-css": "^3.3.0",
+ "constantinople": "^3.0.1",
+ "jstransformer": "1.0.0",
+ "pug-error": "^1.3.2",
+ "pug-walk": "^1.1.7",
+ "resolve": "^1.1.6",
+ "uglify-js": "^2.6.1"
+ }
+ },
+ "pug-lexer": {
+ "version": "4.0.0",
+ "resolved": "http://registry.npm.taobao.org/pug-lexer/download/pug-lexer-4.0.0.tgz",
+ "integrity": "sha1-IQwYRX7y4XYCQnQMXmR715TOwng=",
+ "requires": {
+ "character-parser": "^2.1.1",
+ "is-expression": "^3.0.0",
+ "pug-error": "^1.3.2"
+ }
+ },
+ "pug-linker": {
+ "version": "3.0.5",
+ "resolved": "http://registry.npm.taobao.org/pug-linker/download/pug-linker-3.0.5.tgz",
+ "integrity": "sha1-npp65ABWgtAn3uuWsAD4juuDoC8=",
+ "requires": {
+ "pug-error": "^1.3.2",
+ "pug-walk": "^1.1.7"
+ }
+ },
+ "pug-load": {
+ "version": "2.0.11",
+ "resolved": "http://registry.npm.taobao.org/pug-load/download/pug-load-2.0.11.tgz",
+ "integrity": "sha1-5kjlftET/iwfRdV4WOorrWvAFSc=",
+ "requires": {
+ "object-assign": "^4.1.0",
+ "pug-walk": "^1.1.7"
+ }
+ },
+ "pug-parser": {
+ "version": "5.0.0",
+ "resolved": "http://registry.npm.taobao.org/pug-parser/download/pug-parser-5.0.0.tgz",
+ "integrity": "sha1-45Stmz/KkxI5QK/4hcBuRKt+aOQ=",
+ "requires": {
+ "pug-error": "^1.3.2",
+ "token-stream": "0.0.1"
+ }
+ },
+ "pug-runtime": {
+ "version": "2.0.4",
+ "resolved": "http://registry.npm.taobao.org/pug-runtime/download/pug-runtime-2.0.4.tgz",
+ "integrity": "sha1-4XjhvaaKsujArPybztLFT9iM61g="
+ },
+ "pug-strip-comments": {
+ "version": "1.0.3",
+ "resolved": "http://registry.npm.taobao.org/pug-strip-comments/download/pug-strip-comments-1.0.3.tgz",
+ "integrity": "sha1-8VWVkiBu3G+FMQ2s9K+0igJa9Z8=",
+ "requires": {
+ "pug-error": "^1.3.2"
+ }
+ },
+ "pug-walk": {
+ "version": "1.1.7",
+ "resolved": "http://registry.npm.taobao.org/pug-walk/download/pug-walk-1.1.7.tgz",
+ "integrity": "sha1-wA1cUSi6xYBr7BXSt+fNq+QlMfM="
+ },
+ "punycode": {
+ "version": "1.3.2",
+ "resolved": "http://registry.npm.taobao.org/punycode/download/punycode-1.3.2.tgz",
+ "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
+ },
+ "qcloudapi-sdk": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/qcloudapi-sdk/-/qcloudapi-sdk-0.2.0.tgz",
+ "integrity": "sha512-Hy6a+95zDheaeMbA/N/7AEuAoOhwjR1POPI9xCwfsPc3b24ZzyH1M4PKgO9sq7Rq62+14ZTessHMdXJ+6NaeQg==",
+ "requires": {
+ "dot-qs": "^0.2.0",
+ "object-assign": "^3.0.0",
+ "request": ">= 2.33.0"
+ },
+ "dependencies": {
+ "object-assign": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
+ "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I="
+ }
+ }
+ },
+ "qiniu": {
+ "version": "7.1.3",
+ "resolved": "http://registry.npm.taobao.org/qiniu/download/qiniu-7.1.3.tgz",
+ "integrity": "sha1-Zk2FDSvmndSMEldV2O1XTUh/kzI=",
+ "requires": {
+ "agentkeepalive": "3.3.0",
+ "crc32": "0.2.2",
+ "encodeurl": "^1.0.1",
+ "formstream": "1.1.0",
+ "mime": "1.3.6",
+ "tunnel-agent": "0.6.0",
+ "urllib": "2.22.0"
+ },
+ "dependencies": {
+ "mime": {
+ "version": "1.3.6",
+ "resolved": "http://registry.npm.taobao.org/mime/download/mime-1.3.6.tgz",
+ "integrity": "sha1-WR2E02U6awtKO5343lqoEI5y5eA="
+ }
+ }
+ },
+ "qs": {
+ "version": "6.5.1",
+ "resolved": "http://registry.npm.taobao.org/qs/download/qs-6.5.1.tgz",
+ "integrity": "sha1-NJzfbu+J7EXBLX1es/wMhwNDptg="
+ },
+ "querystring": {
+ "version": "0.2.0",
+ "resolved": "http://registry.npm.taobao.org/querystring/download/querystring-0.2.0.tgz",
+ "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
+ },
+ "rand-token": {
+ "version": "0.4.0",
+ "resolved": "http://registry.npm.taobao.org/rand-token/download/rand-token-0.4.0.tgz",
+ "integrity": "sha1-GlZbatEtkt1LMMTE5ZReiqJKWzs="
+ },
+ "range-parser": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npm.taobao.org/range-parser/download/range-parser-1.2.0.tgz",
+ "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
+ },
+ "raw-body": {
+ "version": "2.3.2",
+ "resolved": "http://registry.npm.taobao.org/raw-body/download/raw-body-2.3.2.tgz",
+ "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
+ "requires": {
+ "bytes": "3.0.0",
+ "http-errors": "1.6.2",
+ "iconv-lite": "0.4.19",
+ "unpipe": "1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.5",
+ "resolved": "http://registry.npm.taobao.org/readable-stream/download/readable-stream-2.3.5.tgz",
+ "integrity": "sha1-tPhQA6k4y7bsvOKhJPsQEr0ag40=",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.0.3",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "recursive-readdir": {
+ "version": "2.2.2",
+ "resolved": "http://registry.npm.taobao.org/recursive-readdir/download/recursive-readdir-2.2.2.tgz",
+ "integrity": "sha1-mUb7MnThYo3m42svZxSVO0hFCU8=",
+ "requires": {
+ "minimatch": "3.0.4"
+ }
+ },
+ "redeyed": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/redeyed/download/redeyed-1.0.1.tgz",
+ "integrity": "sha1-6WwZO0DAgWsArshCaY5hGF5VSYo=",
+ "requires": {
+ "esprima": "~3.0.0"
+ },
+ "dependencies": {
+ "esprima": {
+ "version": "3.0.0",
+ "resolved": "http://registry.npm.taobao.org/esprima/download/esprima-3.0.0.tgz",
+ "integrity": "sha1-U88kes2ncxPlUcOqLnM0LT+099k="
+ }
+ }
+ },
+ "redis": {
+ "version": "2.8.0",
+ "resolved": "http://registry.npm.taobao.org/redis/download/redis-2.8.0.tgz",
+ "integrity": "sha1-ICKI4/WMSfYHnZevehDhMDrhSwI=",
+ "requires": {
+ "double-ended-queue": "^2.1.0-0",
+ "redis-commands": "^1.2.0",
+ "redis-parser": "^2.6.0"
+ }
+ },
+ "redis-commands": {
+ "version": "1.3.5",
+ "resolved": "http://registry.npm.taobao.org/redis-commands/download/redis-commands-1.3.5.tgz",
+ "integrity": "sha1-RJWIlBTx6IYmEYCxRC5ylWAtg6I="
+ },
+ "redis-parser": {
+ "version": "2.6.0",
+ "resolved": "http://registry.npm.taobao.org/redis-parser/download/redis-parser-2.6.0.tgz",
+ "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs="
+ },
+ "referrer-policy": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npm.taobao.org/referrer-policy/download/referrer-policy-1.1.0.tgz",
+ "integrity": "sha1-NXdOtzW/UPtsB46DM0tHI1AgfXk="
+ },
+ "regenerator-runtime": {
+ "version": "0.11.1",
+ "resolved": "http://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.11.1.tgz",
+ "integrity": "sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk="
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "http://registry.npm.taobao.org/repeat-string/download/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
+ },
+ "request": {
+ "version": "2.85.0",
+ "resolved": "http://registry.npm.taobao.org/request/download/request-2.85.0.tgz",
+ "integrity": "sha1-WgNhWkfGFCCz65m326IE+DYD4fo=",
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.6.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.5",
+ "extend": "~3.0.1",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.1",
+ "har-validator": "~5.0.3",
+ "hawk": "~6.0.2",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.17",
+ "oauth-sign": "~0.8.2",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.1",
+ "safe-buffer": "^5.1.1",
+ "stringstream": "~0.0.5",
+ "tough-cookie": "~2.3.3",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.1.0"
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "http://registry.npm.taobao.org/require-directory/download/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+ },
+ "require-main-filename": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/require-main-filename/download/require-main-filename-1.0.1.tgz",
+ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
+ },
+ "resolve": {
+ "version": "1.5.0",
+ "resolved": "http://registry.npm.taobao.org/resolve/download/resolve-1.5.0.tgz",
+ "integrity": "sha1-HwmsznlsmnYlefMbLBzEw83fnzY=",
+ "requires": {
+ "path-parse": "^1.0.5"
+ }
+ },
+ "retry-as-promised": {
+ "version": "2.3.2",
+ "resolved": "http://registry.npm.taobao.org/retry-as-promised/download/retry-as-promised-2.3.2.tgz",
+ "integrity": "sha1-zZdO5P2bX+A8vzGHHuSCIcB3N7c=",
+ "requires": {
+ "bluebird": "^3.4.6",
+ "debug": "^2.6.9"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz",
+ "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "rfdc": {
+ "version": "1.1.2",
+ "resolved": "http://registry.npm.taobao.org/rfdc/download/rfdc-1.1.2.tgz",
+ "integrity": "sha1-5uctdPXcOd6PU49l4Aw2wYAY40k="
+ },
+ "right-align": {
+ "version": "0.1.3",
+ "resolved": "http://registry.npm.taobao.org/right-align/download/right-align-0.1.3.tgz",
+ "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
+ "requires": {
+ "align-text": "^0.1.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.1",
+ "resolved": "http://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.1.tgz",
+ "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM="
+ },
+ "safefs": {
+ "version": "3.2.2",
+ "resolved": "http://registry.npm.taobao.org/safefs/download/safefs-3.2.2.tgz",
+ "integrity": "sha1-gXDBRE1wOOCMrqBaN0+uL6NJ4Vw=",
+ "requires": {
+ "graceful-fs": "*"
+ }
+ },
+ "sax": {
+ "version": "0.6.1",
+ "resolved": "http://registry.npm.taobao.org/sax/download/sax-0.6.1.tgz",
+ "integrity": "sha1-VjsZx8HeiS4Jv8Ty/DDjwn8JUrk="
+ },
+ "scandirectory": {
+ "version": "2.5.0",
+ "resolved": "http://registry.npm.taobao.org/scandirectory/download/scandirectory-2.5.0.tgz",
+ "integrity": "sha1-bOA/VKCQtmjjy+2/IO354xBZPnI=",
+ "requires": {
+ "ignorefs": "^1.0.0",
+ "safefs": "^3.1.2",
+ "taskgroup": "^4.0.5"
+ }
+ },
+ "semver": {
+ "version": "5.5.0",
+ "resolved": "http://registry.npm.taobao.org/semver/download/semver-5.5.0.tgz",
+ "integrity": "sha1-3Eu8emyp2Rbe5dQ1FvAJK1j3uKs="
+ },
+ "send": {
+ "version": "0.16.2",
+ "resolved": "http://registry.npm.taobao.org/send/download/send-0.16.2.tgz",
+ "integrity": "sha1-bsyh4PjBVtFBWXVZhI32RzCmu8E=",
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "~1.6.2",
+ "mime": "1.4.1",
+ "ms": "2.0.0",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.0",
+ "statuses": "~1.4.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz",
+ "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "mime": {
+ "version": "1.4.1",
+ "resolved": "http://registry.npm.taobao.org/mime/download/mime-1.4.1.tgz",
+ "integrity": "sha1-Eh+evEnjdm8xGnbh+hyAA8SwOqY="
+ }
+ }
+ },
+ "seq-queue": {
+ "version": "0.0.5",
+ "resolved": "http://registry.npm.taobao.org/seq-queue/download/seq-queue-0.0.5.tgz",
+ "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4="
+ },
+ "sequelize": {
+ "version": "4.37.1",
+ "resolved": "http://registry.npm.taobao.org/sequelize/download/sequelize-4.37.1.tgz",
+ "integrity": "sha1-F6qX8mm3YhAVxz53qmshNPRdp9c=",
+ "requires": {
+ "bluebird": "^3.5.0",
+ "cls-bluebird": "^2.1.0",
+ "debug": "^3.1.0",
+ "depd": "^1.1.0",
+ "dottie": "^2.0.0",
+ "generic-pool": "^3.4.0",
+ "inflection": "1.12.0",
+ "lodash": "^4.17.1",
+ "moment": "^2.20.0",
+ "moment-timezone": "^0.5.14",
+ "retry-as-promised": "^2.3.2",
+ "semver": "^5.5.0",
+ "terraformer-wkt-parser": "^1.1.2",
+ "toposort-class": "^1.0.1",
+ "uuid": "^3.2.1",
+ "validator": "^9.4.1",
+ "wkx": "^0.4.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz",
+ "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "inflection": {
+ "version": "1.12.0",
+ "resolved": "http://registry.npm.taobao.org/inflection/download/inflection-1.12.0.tgz",
+ "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY="
+ },
+ "uuid": {
+ "version": "3.2.1",
+ "resolved": "http://registry.npm.taobao.org/uuid/download/uuid-3.2.1.tgz",
+ "integrity": "sha1-EsUou51Y0LkmXZovbw/ovhf/HxQ="
+ },
+ "validator": {
+ "version": "9.4.1",
+ "resolved": "http://registry.npm.taobao.org/validator/download/validator-9.4.1.tgz",
+ "integrity": "sha1-q/Rm05i1Yc0kMFARLG/x3mzBJmM="
+ }
+ }
+ },
+ "serve-favicon": {
+ "version": "2.4.5",
+ "resolved": "http://registry.npm.taobao.org/serve-favicon/download/serve-favicon-2.4.5.tgz",
+ "integrity": "sha1-SdmkaGMVOpJAaRyJPSsOfYXW1DY=",
+ "requires": {
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "ms": "2.0.0",
+ "parseurl": "~1.3.2",
+ "safe-buffer": "5.1.1"
+ }
+ },
+ "serve-static": {
+ "version": "1.13.2",
+ "resolved": "http://registry.npm.taobao.org/serve-static/download/serve-static-1.13.2.tgz",
+ "integrity": "sha1-CV6Ecv1bRiN9tQzkhqQ/S4bGzsE=",
+ "requires": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.2",
+ "send": "0.16.2"
+ }
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/set-blocking/download/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+ },
+ "setprototypeof": {
+ "version": "1.0.3",
+ "resolved": "http://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.0.3.tgz",
+ "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npm.taobao.org/shebang-command/download/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/shebang-regex/download/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
+ },
+ "shimmer": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npm.taobao.org/shimmer/download/shimmer-1.2.0.tgz",
+ "integrity": "sha1-+Wb3VVeJdj502IQRk2haXnhzZmU="
+ },
+ "should": {
+ "version": "11.2.1",
+ "resolved": "http://registry.npm.taobao.org/should/download/should-11.2.1.tgz",
+ "integrity": "sha1-kPVRRVUtAc/CAGZuToGKHJZw7aI=",
+ "dev": true,
+ "requires": {
+ "should-equal": "^1.0.0",
+ "should-format": "^3.0.2",
+ "should-type": "^1.4.0",
+ "should-type-adaptors": "^1.0.1",
+ "should-util": "^1.0.0"
+ }
+ },
+ "should-equal": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/should-equal/download/should-equal-1.0.1.tgz",
+ "integrity": "sha1-C26VFvJgGp+wuy3MNpr6HH4gCvc=",
+ "dev": true,
+ "requires": {
+ "should-type": "^1.0.0"
+ }
+ },
+ "should-format": {
+ "version": "3.0.3",
+ "resolved": "http://registry.npm.taobao.org/should-format/download/should-format-3.0.3.tgz",
+ "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=",
+ "dev": true,
+ "requires": {
+ "should-type": "^1.3.0",
+ "should-type-adaptors": "^1.0.1"
+ }
+ },
+ "should-type": {
+ "version": "1.4.0",
+ "resolved": "http://registry.npm.taobao.org/should-type/download/should-type-1.4.0.tgz",
+ "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=",
+ "dev": true
+ },
+ "should-type-adaptors": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npm.taobao.org/should-type-adaptors/download/should-type-adaptors-1.1.0.tgz",
+ "integrity": "sha1-QB5/M7VTMDOUTVzYvytlAneS4no=",
+ "dev": true,
+ "requires": {
+ "should-type": "^1.3.0",
+ "should-util": "^1.0.0"
+ }
+ },
+ "should-util": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/should-util/download/should-util-1.0.0.tgz",
+ "integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=",
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "http://registry.npm.taobao.org/signal-exit/download/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
+ },
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/slash/download/slash-2.0.0.tgz",
+ "integrity": "sha1-3lUoUaF1nfOo8gZTVEL17E3eq0Q="
+ },
+ "sntp": {
+ "version": "2.1.0",
+ "resolved": "http://registry.npm.taobao.org/sntp/download/sntp-2.1.0.tgz",
+ "integrity": "sha1-LGzsFP7cIiJznK+bXD2F0cxaLMg=",
+ "requires": {
+ "hoek": "4.x.x"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.1.1.tgz",
+ "integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw="
+ },
+ "sqlstring": {
+ "version": "2.3.1",
+ "resolved": "http://registry.npm.taobao.org/sqlstring/download/sqlstring-2.3.1.tgz",
+ "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A="
+ },
+ "sshpk": {
+ "version": "1.14.1",
+ "resolved": "http://registry.npm.taobao.org/sshpk/download/sshpk-1.14.1.tgz",
+ "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=",
+ "requires": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "tweetnacl": "~0.14.0"
+ }
+ },
+ "statuses": {
+ "version": "1.4.0",
+ "resolved": "http://registry.npm.taobao.org/statuses/download/statuses-1.4.0.tgz",
+ "integrity": "sha1-u3PURtonlhBu/MG2AaJT1sRr0Ic="
+ },
+ "streamroller": {
+ "version": "0.7.0",
+ "resolved": "http://registry.npm.taobao.org/streamroller/download/streamroller-0.7.0.tgz",
+ "integrity": "sha1-odG3z4PTmvsNYwSaWsv5NJO99ks=",
+ "requires": {
+ "date-format": "^1.2.0",
+ "debug": "^3.1.0",
+ "mkdirp": "^0.5.1",
+ "readable-stream": "^2.3.0"
+ },
+ "dependencies": {
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "http://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ }
+ }
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npm.taobao.org/string-width/download/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.0.3",
+ "resolved": "http://registry.npm.taobao.org/string_decoder/download/string_decoder-1.0.3.tgz",
+ "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "stringstream": {
+ "version": "0.0.5",
+ "resolved": "http://registry.npm.taobao.org/stringstream/download/stringstream-0.0.5.tgz",
+ "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg="
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "http://registry.npm.taobao.org/strip-ansi/download/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-eof": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/strip-eof/download/strip-eof-1.0.0.tgz",
+ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
+ },
+ "superagent": {
+ "version": "3.8.2",
+ "resolved": "http://registry.npm.taobao.org/superagent/download/superagent-3.8.2.tgz",
+ "integrity": "sha1-5KEbnQR/fT7+s7vlNtnsACHRZAM=",
+ "dev": true,
+ "requires": {
+ "component-emitter": "^1.2.0",
+ "cookiejar": "^2.1.0",
+ "debug": "^3.1.0",
+ "extend": "^3.0.0",
+ "form-data": "^2.3.1",
+ "formidable": "^1.1.1",
+ "methods": "^1.1.1",
+ "mime": "^1.4.1",
+ "qs": "^6.5.1",
+ "readable-stream": "^2.0.5"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz",
+ "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "http://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz",
+ "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=",
+ "dev": true
+ }
+ }
+ },
+ "supertest": {
+ "version": "3.0.0",
+ "resolved": "http://registry.npm.taobao.org/supertest/download/supertest-3.0.0.tgz",
+ "integrity": "sha1-jUu2j9GDDuBwM7HFpamkAhyWUpY=",
+ "dev": true,
+ "requires": {
+ "methods": "~1.1.2",
+ "superagent": "^3.0.0"
+ }
+ },
+ "supervisor": {
+ "version": "0.12.0",
+ "resolved": "http://registry.npm.taobao.org/supervisor/download/supervisor-0.12.0.tgz",
+ "integrity": "sha1-3n5jNwFbKRhRwQ81OMSn8EkX7ME=",
+ "dev": true
+ },
+ "taskgroup": {
+ "version": "4.3.1",
+ "resolved": "http://registry.npm.taobao.org/taskgroup/download/taskgroup-4.3.1.tgz",
+ "integrity": "sha1-feGT/r12gnPEV3MElwJNUSwnkVo=",
+ "requires": {
+ "ambi": "^2.2.0",
+ "csextends": "^1.0.3"
+ }
+ },
+ "terraformer": {
+ "version": "1.0.8",
+ "resolved": "http://registry.npm.taobao.org/terraformer/download/terraformer-1.0.8.tgz",
+ "integrity": "sha1-UeCtiXRvzyFh3G9lqnDkI3fItZM=",
+ "requires": {
+ "@types/geojson": "^1.0.0"
+ }
+ },
+ "terraformer-wkt-parser": {
+ "version": "1.1.2",
+ "resolved": "http://registry.npm.taobao.org/terraformer-wkt-parser/download/terraformer-wkt-parser-1.1.2.tgz",
+ "integrity": "sha1-M2oMj8gglKWv+DKI9prt7NNpvww=",
+ "requires": {
+ "terraformer": "~1.0.5"
+ }
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "http://registry.npm.taobao.org/through/download/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
+ },
+ "to-fast-properties": {
+ "version": "1.0.3",
+ "resolved": "http://registry.npm.taobao.org/to-fast-properties/download/to-fast-properties-1.0.3.tgz",
+ "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc="
+ },
+ "token-stream": {
+ "version": "0.0.1",
+ "resolved": "http://registry.npm.taobao.org/token-stream/download/token-stream-0.0.1.tgz",
+ "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo="
+ },
+ "toposort-class": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/toposort-class/download/toposort-class-1.0.1.tgz",
+ "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg="
+ },
+ "tough-cookie": {
+ "version": "2.3.4",
+ "resolved": "http://registry.npm.taobao.org/tough-cookie/download/tough-cookie-2.3.4.tgz",
+ "integrity": "sha1-7GDO44rGdQY//JelwYlwV47oNlU=",
+ "requires": {
+ "punycode": "^1.4.1"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "http://registry.npm.taobao.org/punycode/download/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
+ }
+ }
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "http://registry.npm.taobao.org/tunnel-agent/download/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "http://registry.npm.taobao.org/tweetnacl/download/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "optional": true
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "http://registry.npm.taobao.org/type-check/download/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ },
+ "type-is": {
+ "version": "1.6.16",
+ "resolved": "http://registry.npm.taobao.org/type-is/download/type-is-1.6.16.tgz",
+ "integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=",
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.18"
+ }
+ },
+ "typechecker": {
+ "version": "2.1.0",
+ "resolved": "http://registry.npm.taobao.org/typechecker/download/typechecker-2.1.0.tgz",
+ "integrity": "sha1-0cIJOlT/ihn1jP+HfuqlTyJC04M="
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "http://registry.npm.taobao.org/typedarray/download/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+ },
+ "uc.micro": {
+ "version": "1.0.5",
+ "resolved": "http://registry.npm.taobao.org/uc.micro/download/uc.micro-1.0.5.tgz",
+ "integrity": "sha1-DGXxX4FaoItWCmHOi023/8P0U3Y="
+ },
+ "uglify-js": {
+ "version": "2.8.29",
+ "resolved": "http://registry.npm.taobao.org/uglify-js/download/uglify-js-2.8.29.tgz",
+ "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+ "requires": {
+ "source-map": "~0.5.1",
+ "uglify-to-browserify": "~1.0.0",
+ "yargs": "~3.10.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "1.2.1",
+ "resolved": "http://registry.npm.taobao.org/camelcase/download/camelcase-1.2.1.tgz",
+ "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk="
+ },
+ "cliui": {
+ "version": "2.1.0",
+ "resolved": "http://registry.npm.taobao.org/cliui/download/cliui-2.1.0.tgz",
+ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+ "requires": {
+ "center-align": "^0.1.1",
+ "right-align": "^0.1.1",
+ "wordwrap": "0.0.2"
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+ },
+ "window-size": {
+ "version": "0.1.0",
+ "resolved": "http://registry.npm.taobao.org/window-size/download/window-size-0.1.0.tgz",
+ "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0="
+ },
+ "wordwrap": {
+ "version": "0.0.2",
+ "resolved": "http://registry.npm.taobao.org/wordwrap/download/wordwrap-0.0.2.tgz",
+ "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8="
+ },
+ "yargs": {
+ "version": "3.10.0",
+ "resolved": "http://registry.npm.taobao.org/yargs/download/yargs-3.10.0.tgz",
+ "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
+ "requires": {
+ "camelcase": "^1.0.2",
+ "cliui": "^2.1.0",
+ "decamelize": "^1.0.0",
+ "window-size": "0.1.0"
+ }
+ }
+ }
+ },
+ "uglify-to-browserify": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npm.taobao.org/uglify-to-browserify/download/uglify-to-browserify-1.0.2.tgz",
+ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
+ "optional": true
+ },
+ "unique-string": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
+ "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
+ "requires": {
+ "crypto-random-string": "^1.0.0"
+ }
+ },
+ "universalify": {
+ "version": "0.1.2",
+ "resolved": "http://registry.npm.taobao.org/universalify/download/universalify-0.1.2.tgz",
+ "integrity": "sha1-tkb2m+OULavOzJ1mOcgNwQXvqmY="
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/unpipe/download/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+ },
+ "upyun": {
+ "version": "3.3.9",
+ "resolved": "https://registry.npmjs.org/upyun/-/upyun-3.3.9.tgz",
+ "integrity": "sha512-UqvSKvvFgbQwLI+yjTO0WUnmS2i2KIvX4N2Je6ZTRN4cja17DHX7ARd13DO6h65/JOQJPs/Nua2zlnixNFIdWA==",
+ "requires": {
+ "axios": "^0.18.0",
+ "base-64": "^0.1.0",
+ "form-data": "^2.1.4",
+ "hmacsha1": "^1.0.0",
+ "is-promise": "^2.1.0",
+ "md5": "^2.2.1",
+ "mime-types": "^2.1.15"
+ }
+ },
+ "url": {
+ "version": "0.10.3",
+ "resolved": "http://registry.npm.taobao.org/url/download/url-0.10.3.tgz",
+ "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
+ "requires": {
+ "punycode": "1.3.2",
+ "querystring": "0.2.0"
+ }
+ },
+ "urllib": {
+ "version": "2.22.0",
+ "resolved": "http://registry.npm.taobao.org/urllib/download/urllib-2.22.0.tgz",
+ "integrity": "sha1-KWXcSuEnpvtpW32yfTGE8X2Cy0I=",
+ "requires": {
+ "any-promise": "^1.3.0",
+ "content-type": "^1.0.2",
+ "debug": "^2.6.0",
+ "default-user-agent": "^1.0.0",
+ "digest-header": "^0.0.1",
+ "ee-first": "~1.1.1",
+ "humanize-ms": "^1.2.0",
+ "iconv-lite": "^0.4.15",
+ "qs": "^6.4.0",
+ "statuses": "^1.3.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz",
+ "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+ },
+ "utility": {
+ "version": "0.1.11",
+ "resolved": "http://registry.npm.taobao.org/utility/download/utility-0.1.11.tgz",
+ "integrity": "sha1-/eYM+bTkdRlHoM9dEEzik2ciZxU=",
+ "requires": {
+ "address": ">=0.0.1"
+ }
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+ },
+ "uuid": {
+ "version": "3.1.0",
+ "resolved": "http://registry.npm.taobao.org/uuid/download/uuid-3.1.0.tgz",
+ "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ="
+ },
+ "validator": {
+ "version": "10.6.0",
+ "resolved": "http://registry.npm.taobao.org/validator/download/validator-10.6.0.tgz",
+ "integrity": "sha1-qb3OaFs8PoSA5+u7nrlcVM2XM7A="
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "http://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "http://registry.npm.taobao.org/verror/download/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "void-elements": {
+ "version": "2.0.1",
+ "resolved": "http://registry.npm.taobao.org/void-elements/download/void-elements-2.0.1.tgz",
+ "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w="
+ },
+ "watchr": {
+ "version": "2.4.13",
+ "resolved": "http://registry.npm.taobao.org/watchr/download/watchr-2.4.13.tgz",
+ "integrity": "sha1-10hHu01vkPYf4sdPn2hmKqDgdgE=",
+ "requires": {
+ "eachr": "^2.0.2",
+ "extendr": "^2.1.0",
+ "extract-opts": "^2.2.0",
+ "ignorefs": "^1.0.0",
+ "safefs": "^3.1.2",
+ "scandirectory": "^2.5.0",
+ "taskgroup": "^4.2.0",
+ "typechecker": "^2.0.8"
+ }
+ },
+ "which": {
+ "version": "1.3.0",
+ "resolved": "http://registry.npm.taobao.org/which/download/which-1.3.0.tgz",
+ "integrity": "sha1-/wS9/AEO5UfXgL7DjhrBwnd9JTo=",
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/which-module/download/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
+ },
+ "win-release": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npm.taobao.org/win-release/download/win-release-1.1.1.tgz",
+ "integrity": "sha1-X6VeAr58qTTt/BJmVjLoSbcuUgk=",
+ "requires": {
+ "semver": "^5.0.1"
+ }
+ },
+ "window-size": {
+ "version": "0.1.4",
+ "resolved": "http://registry.npm.taobao.org/window-size/download/window-size-0.1.4.tgz",
+ "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY="
+ },
+ "with": {
+ "version": "5.1.1",
+ "resolved": "http://registry.npm.taobao.org/with/download/with-5.1.1.tgz",
+ "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=",
+ "requires": {
+ "acorn": "^3.1.0",
+ "acorn-globals": "^3.0.0"
+ }
+ },
+ "wkx": {
+ "version": "0.4.4",
+ "resolved": "http://registry.npm.taobao.org/wkx/download/wkx-0.4.4.tgz",
+ "integrity": "sha1-z3UbZy5LReFi+f0wEkh45z2WybI=",
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npm.taobao.org/wordwrap/download/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "2.1.0",
+ "resolved": "http://registry.npm.taobao.org/wrap-ansi/download/wrap-ansi-2.1.0.tgz",
+ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+ "requires": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ },
+ "write-file-atomic": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz",
+ "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==",
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "x-xss-protection": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npm.taobao.org/x-xss-protection/download/x-xss-protection-1.1.0.tgz",
+ "integrity": "sha1-TxiYwzLesefyvhKA77PixT1pwac="
+ },
+ "xdg-basedir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
+ "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ="
+ },
+ "xml2js": {
+ "version": "0.4.4",
+ "resolved": "http://registry.npm.taobao.org/xml2js/download/xml2js-0.4.4.tgz",
+ "integrity": "sha1-MREBAAMAiuGSQOuhdJe1fHKcVV0=",
+ "requires": {
+ "sax": "0.6.x",
+ "xmlbuilder": ">=1.0.0"
+ }
+ },
+ "xmlbuilder": {
+ "version": "2.6.5",
+ "resolved": "http://registry.npm.taobao.org/xmlbuilder/download/xmlbuilder-2.6.5.tgz",
+ "integrity": "sha1-b/etYPty0idk8AehZLd/K/FABSY=",
+ "requires": {
+ "lodash": "^3.5.0"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "3.10.1",
+ "resolved": "http://registry.npm.taobao.org/lodash/download/lodash-3.10.1.tgz",
+ "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y="
+ }
+ }
+ },
+ "xregexp": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/xregexp/download/xregexp-2.0.0.tgz",
+ "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM="
+ },
+ "xtend": {
+ "version": "4.0.1",
+ "resolved": "http://registry.npm.taobao.org/xtend/download/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
+ },
+ "y18n": {
+ "version": "3.2.1",
+ "resolved": "http://registry.npm.taobao.org/y18n/download/y18n-3.2.1.tgz",
+ "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "http://registry.npm.taobao.org/yallist/download/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+ },
+ "yargs": {
+ "version": "12.0.1",
+ "resolved": "http://registry.npm.taobao.org/yargs/download/yargs-12.0.1.tgz",
+ "integrity": "sha1-ZDLlYSO7Tnw1YhFUAemDdAYCYcI=",
+ "requires": {
+ "cliui": "^4.0.0",
+ "decamelize": "^2.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^2.0.0",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^2.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^3.2.1 || ^4.0.0",
+ "yargs-parser": "^10.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "http://registry.npm.taobao.org/ansi-regex/download/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+ },
+ "cliui": {
+ "version": "4.1.0",
+ "resolved": "http://registry.npm.taobao.org/cliui/download/cliui-4.1.0.tgz",
+ "integrity": "sha1-NIQi2+gtgAswIu709qwQvy5NG0k=",
+ "requires": {
+ "string-width": "^2.1.1",
+ "strip-ansi": "^4.0.0",
+ "wrap-ansi": "^2.0.0"
+ }
+ },
+ "decamelize": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/decamelize/download/decamelize-2.0.0.tgz",
+ "integrity": "sha1-ZW17vICUxMeI6lPFhAkIycfQY8c=",
+ "requires": {
+ "xregexp": "4.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+ },
+ "os-locale": {
+ "version": "2.1.0",
+ "resolved": "http://registry.npm.taobao.org/os-locale/download/os-locale-2.1.0.tgz",
+ "integrity": "sha1-QrwpAKa1uL0XN2yOiCtlr8zyS/I=",
+ "requires": {
+ "execa": "^0.7.0",
+ "lcid": "^1.0.0",
+ "mem": "^1.1.0"
+ }
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "http://registry.npm.taobao.org/string-width/download/string-width-2.1.1.tgz",
+ "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=",
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "http://registry.npm.taobao.org/strip-ansi/download/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ },
+ "xregexp": {
+ "version": "4.0.0",
+ "resolved": "http://registry.npm.taobao.org/xregexp/download/xregexp-4.0.0.tgz",
+ "integrity": "sha1-5pgYneSd0qGMxWh7BeF8jkOUMCA="
+ }
+ }
+ },
+ "yargs-parser": {
+ "version": "10.1.0",
+ "resolved": "http://registry.npm.taobao.org/yargs-parser/download/yargs-parser-10.1.0.tgz",
+ "integrity": "sha1-cgImW4n36eny5XZeD+c1qQXtuqg=",
+ "requires": {
+ "camelcase": "^4.1.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "4.1.0",
+ "resolved": "http://registry.npm.taobao.org/camelcase/download/camelcase-4.1.0.tgz",
+ "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
+ }
+ }
+ },
+ "yauzl": {
+ "version": "2.4.1",
+ "resolved": "http://registry.npm.taobao.org/yauzl/download/yauzl-2.4.1.tgz",
+ "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=",
+ "requires": {
+ "fd-slicer": "~1.0.1"
+ }
+ },
+ "yazl": {
+ "version": "2.4.3",
+ "resolved": "http://registry.npm.taobao.org/yazl/download/yazl-2.4.3.tgz",
+ "integrity": "sha1-7CblzIfVYBud+EMtvdPNLlFzoHE=",
+ "requires": {
+ "buffer-crc32": "~0.2.3"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index d1b0605d..1e3fc19a 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "code-push-server",
"description": "CodePush service is hotupdate services which adapter react-native-code-push and cordova-plugin-code-push",
- "version": "0.2.19",
+ "version": "0.5.4",
"license": "MIT",
"repository": {
"type": "git",
@@ -34,43 +34,47 @@
"init": "node ./bin/db init",
"upgrade": "node ./bin/db upgrade",
"test": "make test",
- "test-win": "mocha test/api/init test/api/users test/api/auth test/api/account test/api/accessKeys test/api/sessions test/api/apps test/api/index --recursive --timeout 15000",
+ "test-win": "mocha test/api/init test/api/users test/api/auth test/api/account test/api/accessKeys test/api/apps test/api/index --recursive --timeout 15000",
"coverage": "make coverage"
},
"dependencies": {
"aliyun-oss-upload-stream": "^1.3.0",
- "aliyun-sdk": "^1.9.17",
+ "aliyun-sdk": "^1.11.10",
"aws-sdk": "^2.7.0",
"bcryptjs": "^2.3.0",
"bluebird": "^3.4.1",
"body-parser": "^1.15.2",
"cookie-parser": "^1.4.3",
- "debug": "^2.3.3",
+ "cos-nodejs-sdk-v5": "^2.4.10",
+ "debug": "^3.1.0",
+ "diff-match-patch": "^1.0.1",
"express": "^4.14.0",
"extract-zip": "^1.6.0",
- "formidable": "^1.0.17",
- "fs-extra": "^2.0.0",
+ "formidable": "^1.2.1",
+ "fs-extra": "^7.0.0",
"helmet": "^3.1.0",
"i18n": "^0.8.3",
- "jsonwebtoken": "^7.1.7",
- "lodash": "^4.5.1",
- "log4js": "^1.1.0",
+ "jschardet": "^1.6.0",
+ "jsonwebtoken": "^8.2.0",
+ "lodash": "^4.17.5",
+ "log4js": "^3.0.5",
"markdown-it": "^8.0.1",
"moment": "^2.14.1",
"morgan": "^1.7.0",
- "mysql": "^2.10.2",
- "nodemailer": "^3.1.3",
- "pug": "^2.0.0-beta6",
- "qiniu": "^6.1.10",
- "rand-token": "^0.3.0",
- "recursive-fs": "^1.0.0",
+ "mysql2": "^1.3.5",
+ "nodemailer": "^4.0.1",
+ "pug": "^2.0.1",
+ "qiniu": "^7.1.3",
+ "upyun": "^3.3.9",
+ "rand-token": "^0.4.0",
+ "recursive-readdir": "^2.1.1",
"redis": "^2.6.2",
"request": "^2.72.0",
- "sequelize": "^3.24.5",
+ "sequelize": "^4.37.1",
"serve-favicon": "^2.4.0",
- "slash": "^1.0.0",
- "validator": "^7.0.0",
- "yargs": "^6.2.0",
+ "slash": "^2.0.0",
+ "validator": "^10.6.0",
+ "yargs": "^12.0.1",
"yazl": "^2.3.0"
},
"devDependencies": {
@@ -85,6 +89,7 @@
"config",
"core",
"docs",
+ "docker",
"models",
"public",
"routes",
diff --git a/routes/accessKeys.js b/routes/accessKeys.js
index 835ac8ff..b7239b41 100644
--- a/routes/accessKeys.js
+++ b/routes/accessKeys.js
@@ -19,12 +19,7 @@ router.get('/', middleware.checkToken, (req, res, next) => {
res.send({accessKeys: accessKeys});
})
.catch((e) => {
- if (e instanceof AppError.AppError) {
- log.debug('request get acceesKeys AppError', e)
- res.status(406).send(e.message);
- } else {
- next(e);
- }
+ next(e);
});
});
@@ -33,9 +28,9 @@ router.post('/', middleware.checkToken, (req, res, next) => {
var identical = req.users.identical;
var createdBy = _.trim(req.body.createdBy);
var friendlyName = _.trim(req.body.friendlyName);
- var isSession = req.body.isSession;
var ttl = parseInt(req.body.ttl);
var description = _.trim(req.body.description);
+ log.debug(req.body)
var newAccessKey = security.randToken(28).concat(identical);
return accountManager.isExsitAccessKeyName(uid, friendlyName)
.then((data) => {
@@ -44,7 +39,7 @@ router.post('/', middleware.checkToken, (req, res, next) => {
}
})
.then(() => {
- return accountManager.createAccessKey(uid, newAccessKey, isSession, ttl, friendlyName, createdBy, description);
+ return accountManager.createAccessKey(uid, newAccessKey, ttl, friendlyName, createdBy, description);
})
.then((newToken) => {
var moment = require("moment");
@@ -53,14 +48,15 @@ router.post('/', middleware.checkToken, (req, res, next) => {
createdTime : parseInt(moment(newToken.created_at).format('x')),
createdBy : newToken.created_by,
expires : parseInt(moment(newToken.expires_at).format('x')),
- isSession: newToken.is_session == 1 ? true :false,
description : newToken.description,
friendlyName: newToken.name,
};
+ log.debug(info);
res.send({accessKey:info});
})
.catch((e) => {
if (e instanceof AppError.AppError) {
+ log.debug(e)
res.status(406).send(e.message);
} else {
next(e);
@@ -73,66 +69,16 @@ router.delete('/:name', middleware.checkToken, (req, res, next) => {
var uid = req.users.id;
return models.UserTokens.destroy({where: {name:name, uid: uid}})
.then((rowNum) => {
+ log.debug('delete acceesKey:', name)
res.send({friendlyName:name});
})
.catch((e) => {
if (e instanceof AppError.AppError) {
+ log.debug(e)
res.status(406).send(e.message);
} else {
next(e);
}
});
});
-
-router.patch('/:name', middleware.checkToken, (req, res, next) => {
- var name = _.trim(decodeURI(req.params.name));
- var friendlyName = _.trim(req.body.friendlyName);
- var ttl = _.trim(req.body.ttl);
- var uid = req.users.id;
- return Promise.all([
- models.UserTokens.findOne({where: {name:name, uid: uid}}),
- accountManager.isExsitAccessKeyName(uid, friendlyName),
- ])
- .spread((token, token2) => {
- if (_.isEmpty(token)) {
- throw new AppError.AppError(`The access key "${name}" does not exist.`);
- }
- if (!_.isEmpty(token2)) {
- throw new AppError.AppError(`The access key "${friendlyName}" already exists.`);
- }
- return token;
- })
- .then((token) => {
- var moment = require('moment');
- if (ttl > 0 || ttl < 0) {
- var newExp = moment(token.get('expires_at')).add(ttl/1000, 'seconds').format('YYYY-MM-DD HH:mm:ss');
- token.set('expires_at', newExp);
- }
- if (friendlyName.length > 0) {
- token.set('name', friendlyName);
- }
- return token.save();
- })
- .then((token) => {
- var info = {
- name : '(hidden)',
- isSession: token.is_session == 1 ? true :false,
- createdTime : token.created_at,
- createdBy : token.created_by,
- description : token.description,
- expires : token.expires_at,
- friendlyName: token.name,
- id: token.id + ""
- };
- res.send({accessKey: info});
- })
- .catch((e) => {
- if (e instanceof AppError.AppError) {
- res.status(406).send(e.message);
- } else {
- next(e);
- }
- });
-});
-
module.exports = router;
diff --git a/routes/account.js b/routes/account.js
index c022af4d..edae56f1 100644
--- a/routes/account.js
+++ b/routes/account.js
@@ -4,14 +4,16 @@ var models = require('../models');
var _ = require('lodash');
var security = require('../core/utils/security');
var middleware = require('../core/middleware');
+var log4js = require('log4js');
+var log = log4js.getLogger("cps:account");
router.get('/', middleware.checkToken, (req, res) => {
var userInfo = {
email:req.users.email,
- id:req.users.identical,
linkedProviders: [],
name:req.users.username,
};
+ log.debug(userInfo);
res.send({account:userInfo});
});
diff --git a/routes/apps.js b/routes/apps.js
index 8f634bec..8fd7d832 100644
--- a/routes/apps.js
+++ b/routes/apps.js
@@ -15,13 +15,10 @@ var config = require('../core/config');
const REGEX = /^(\w+)(-android|-ios)$/;
const REGEX_ANDROID = /^(\w+)(-android)$/;
const REGEX_IOS = /^(\w+)(-ios)$/;
-const OLD_REGEX_ANDROID = /^(android_)/;
-const OLD_REGEX_IOS = /^(ios_)/;
var log4js = require('log4js');
var log = log4js.getLogger("cps:apps");
-router.get('/',
- middleware.checkToken, (req, res, next) => {
+router.get('/', middleware.checkToken, (req, res, next) => {
var uid = req.users.id;
var appManager = new AppManager();
appManager.listApps(uid)
@@ -73,6 +70,7 @@ router.get('/:appName/deployments/:deploymentName',
throw new AppError.AppError("does not find the deployment");
}
res.send({deployment: deployments.listDeloyment(deploymentInfo)});
+ return true;
})
.catch((e) => {
if (e instanceof AppError.AppError) {
@@ -173,7 +171,7 @@ router.get('/:appName/deployments/:deploymentName/history',
return deployments.getDeploymentHistory(deploymentInfo.id);
})
.then((rs) => {
- res.send({history: rs});
+ res.send({history: _.pullAll(rs, [null, false])});
})
.catch((e) => {
if (e instanceof AppError.AppError) {
@@ -269,17 +267,7 @@ router.post('/:appName/deployments/:deploymentName/release',
var packageManager = new PackageManager();
accountManager.collaboratorCan(uid, appName)
.then((col) => {
- var pubType = '';
- log.debug(`check publish type`);
- if (REGEX_ANDROID.test(appName)) {
- pubType = 'android';
- } else if (REGEX_IOS.test(appName)) {
- pubType = 'ios';
- } else {
- log.debug(`you have to rename app name, eg. Demo-android Demo-ios`);
- throw new AppError.AppError(`you have to rename app name, eg. Demo-android Demo-ios`);
- }
- log.debug(`publish type is ${pubType}`);
+ log.debug(col);
return deployments.findDeloymentByName(deploymentName, col.appid)
.then((deploymentInfo) => {
if (_.isEmpty(deploymentInfo)) {
@@ -288,18 +276,23 @@ router.post('/:appName/deployments/:deploymentName/release',
}
return packageManager.parseReqFile(req)
.then((data) => {
- return packageManager.releasePackage(deploymentInfo.id, data.packageInfo, data.package.type, data.package.path, uid, pubType)
+ if (data.package.type != "application/zip") {
+ log.debug(`upload file type is invlidate`, data.package);
+ throw new AppError.AppError("upload file type is invalidate");
+ }
+ log.debug('packageInfo:', data.packageInfo);
+ return packageManager.releasePackage(deploymentInfo.appid, deploymentInfo.id, data.packageInfo, data.package.path, uid)
.finally(() => {
common.deleteFolderSync(data.package.path);
});
})
.then((packages) => {
if (packages) {
- Promise.delay(2000)
+ Promise.delay(1000)
.then(() => {
- packageManager.createDiffPackagesByLastNums(packages.id, _.get(config, 'common.diffNums', 1))
+ packageManager.createDiffPackagesByLastNums(deploymentInfo.appid, packages, _.get(config, 'common.diffNums', 1))
.catch((e) => {
- console.error(e);
+ log.error(e);
});
});
}
@@ -316,8 +309,8 @@ router.post('/:appName/deployments/:deploymentName/release',
});
});
})
- .then((data) => {
- res.send("");
+ .then(() => {
+ res.send('{"msg": "succeed"}');
})
.catch((e) => {
if (e instanceof AppError.AppError) {
@@ -330,12 +323,13 @@ router.post('/:appName/deployments/:deploymentName/release',
router.patch('/:appName/deployments/:deploymentName/release',
middleware.checkToken, (req, res, next) => {
- return res.status(406).send('Not supported currently');
+ log.debug('req.body', req.body);
var appName = _.trim(req.params.appName);
var deploymentName = _.trim(req.params.deploymentName);
var uid = req.users.id;
var deployments = new Deployments();
var packageManager = new PackageManager();
+ var label = _.get(req, 'body.packageInfo.label');
accountManager.collaboratorCan(uid, appName)
.then((col) => {
return deployments.findDeloymentByName(deploymentName, col.appid)
@@ -343,9 +337,35 @@ router.patch('/:appName/deployments/:deploymentName/release',
if (_.isEmpty(deploymentInfo)) {
throw new AppError.AppError("does not find the deployment");
}
- var label = deploymentInfo.label;
- var deploymentVersionId = deploymentInfo.last_deployment_version_id;
- return packageManager.modifyReleasePackage(deploymentInfo.id, deploymentVersionId, _.get(req, 'body.packageInfo'));
+ if (label) {
+ return packageManager.findPackageInfoByDeploymentIdAndLabel(deploymentInfo.id, label)
+ .then((data)=>{
+ return [deploymentInfo, data];
+ });
+ } else {
+ var deploymentVersionId = deploymentInfo.last_deployment_version_id;
+ return packageManager.findLatestPackageInfoByDeployVersion(deploymentVersionId)
+ .then((data)=>{
+ return [deploymentInfo, data];
+ });;
+ }
+ })
+ .spread((deploymentInfo, packageInfo)=>{
+ if (!packageInfo) {
+ throw new AppError.AppError("does not find the packageInfo");
+ }
+ return packageManager.modifyReleasePackage(packageInfo.id, _.get(req, 'body.packageInfo'))
+ .then(()=>{
+ //clear cache if exists.
+ if (_.get(config, 'common.updateCheckCache', false) !== false) {
+ Promise.delay(2500)
+ .then(() => {
+ var ClientManager = require('../core/services/client-manager');
+ var clientManager = new ClientManager();
+ clientManager.clearUpdateCheckCache(deploymentInfo.deployment_key, '*', '*', '*');
+ });
+ }
+ });
});
}).then((data) => {
res.send("");
@@ -359,8 +379,10 @@ router.patch('/:appName/deployments/:deploymentName/release',
});
});
+
router.post('/:appName/deployments/:sourceDeploymentName/promote/:destDeploymentName',
middleware.checkToken, (req, res, next) => {
+ log.debug('req.body:', req.body);
var appName = _.trim(req.params.appName);
var sourceDeploymentName = _.trim(req.params.sourceDeploymentName);
var destDeploymentName = _.trim(req.params.destDeploymentName);
@@ -381,6 +403,23 @@ router.post('/:appName/deployments/:sourceDeploymentName/promote/:destDeployment
if (!destDeploymentInfo) {
throw new AppError.AppError(`${destDeploymentName} does not exist.`);
}
+ return [sourceDeploymentInfo, destDeploymentInfo];
+ })
+ .spread((sourceDeploymentInfo, destDeploymentInfo) => {
+ var params = _.get(req.body, 'packageInfo', {});
+ _.set(params, 'promoteUid', uid);
+ return [packageManager.promotePackage(sourceDeploymentInfo, destDeploymentInfo, params),destDeploymentInfo];
+ })
+ .spread((packages, destDeploymentInfo) => {
+ if (packages) {
+ Promise.delay(1000)
+ .then(() => {
+ packageManager.createDiffPackagesByLastNums(destDeploymentInfo.appid, packages, _.get(config, 'common.diffNums', 1))
+ .catch((e) => {
+ log.error(e);
+ });
+ });
+ }
//clear cache if exists.
if (_.get(config, 'common.updateCheckCache', false) !== false) {
Promise.delay(2500)
@@ -390,26 +429,11 @@ router.post('/:appName/deployments/:sourceDeploymentName/promote/:destDeployment
clientManager.clearUpdateCheckCache(destDeploymentInfo.deployment_key, '*', '*', '*');
});
}
- return [sourceDeploymentInfo.id, destDeploymentInfo.id];
+ return packages;
})
- .spread((sourceDeploymentId, destDeploymentId) => {
- return packageManager.promotePackage(sourceDeploymentId, destDeploymentId, uid);
- });
})
.then((packages) => {
- if (packages) {
- Promise.delay(2000)
- .then(() => {
- packageManager.createDiffPackagesByLastNums(packages.id, _.get(config, 'common.diffNums', 1))
- .catch((e) => {
- console.log(e);
- });
- });
- }
- return null;
- })
- .then(() => {
- res.send('ok');
+ res.send({package:packages});
})
.catch((e) => {
if (e instanceof AppError.AppError) {
@@ -432,16 +456,28 @@ var rollbackCb = function (req, res, next) {
return deployments.findDeloymentByName(deploymentName, col.appid);
})
.then((dep) => {
- //clear cache if exists.
- if (_.get(config, 'common.updateCheckCache', false) !== false) {
- Promise.delay(2500)
- .then(() => {
- var ClientManager = require('../core/services/client-manager');
- var clientManager = new ClientManager();
- clientManager.clearUpdateCheckCache(dep.deployment_key, '*', '*', '*');
- });
- }
- return packageManager.rollbackPackage(dep.last_deployment_version_id, targetLabel, uid);
+ return packageManager.rollbackPackage(dep.last_deployment_version_id, targetLabel, uid)
+ .then((packageInfo)=>{
+ if (packageInfo) {
+ Promise.delay(1000)
+ .then(() => {
+ packageManager.createDiffPackagesByLastNums(dep.appid, packageInfo, 1)
+ .catch((e) => {
+ log.error(e);
+ });
+ });
+ }
+ //clear cache if exists.
+ if (_.get(config, 'common.updateCheckCache', false) !== false) {
+ Promise.delay(2500)
+ .then(() => {
+ var ClientManager = require('../core/services/client-manager');
+ var clientManager = new ClientManager();
+ clientManager.clearUpdateCheckCache(dep.deployment_key, '*', '*', '*');
+ });
+ }
+ return packageInfo;
+ });
})
.then(() => {
res.send('ok');
@@ -583,17 +619,6 @@ router.patch('/:appName',
var appManager = new AppManager();
return accountManager.ownerCan(uid, appName)
.then((col) => {
- if (REGEX_ANDROID.test(appName) || OLD_REGEX_ANDROID.test(appName)) {
- if (!REGEX_ANDROID.test(newAppName)) {
- throw new AppError.AppError(`new appName have to point -android suffix! eg. Demo-android`);
- }
- } else if (REGEX_IOS.test(appName) || OLD_REGEX_IOS.test(appName)) {
- if (!REGEX_IOS.test(newAppName)) {
- throw new AppError.AppError(`new appName have to point -ios suffix! eg. Demo-ios`);
- }
- } else {
- throw new AppError.AppError(`appName have to point -android or -ios suffix! eg. ${appName}-android ${appName}-ios`);
- }
return appManager.findAppByName(uid, newAppName)
.then((appInfo) => {
if (!_.isEmpty(appInfo)){
@@ -647,21 +672,42 @@ router.post('/:appName/transfer/:email',
});
router.post('/', middleware.checkToken, (req, res, next) => {
+ log.debug("addApp params:",req.body);
+ var constName = require('../core/const');
var appName = req.body.name;
- var uid = req.users.id;
- var appManager = new AppManager();
if (_.isEmpty(appName)) {
return res.status(406).send("Please input name!");
}
+ var osName = _.toLower(req.body.os);
+ var os;
+ if (osName == _.toLower(constName.IOS_NAME)) {
+ os = constName.IOS;
+ } else if (osName == _.toLower(constName.ANDROID_NAME)) {
+ os = constName.ANDROID;
+ } else if (osName == _.toLower(constName.WINDOWS_NAME)) {
+ os = constName.WINDOWS;
+ } else {
+ return res.status(406).send("Please input os [iOS|Android|Windows]!");
+ }
+ var platformName = _.toLower(req.body.platform);
+ var platform;
+ if (platformName == _.toLower(constName.REACT_NATIVE_NAME)) {
+ platform = constName.REACT_NATIVE;
+ } else if (platformName == _.toLower(constName.CORDOVA_NAME)) {
+ platform = constName.CORDOVA;
+ } else {
+ return res.status(406).send("Please input platform [React-Native|Cordova]!");
+ }
+ var manuallyProvisionDeployments = req.body.manuallyProvisionDeployments;
+ var uid = req.users.id;
+ var appManager = new AppManager();
+
appManager.findAppByName(uid, appName)
.then((appInfo) => {
if (!_.isEmpty(appInfo)){
throw new AppError.AppError(appName + " Exist!");
}
- if (!REGEX.test(appName)) {
- throw new AppError.AppError(`appName have to point -android or -ios suffix! eg. ${appName}-android ${appName}-ios`);
- }
- return appManager.addApp(uid, appName, req.users.identical)
+ return appManager.addApp(uid, appName, os, platform, req.users.identical)
.then(() => {
return {name: appName, collaborators: {[req.users.email]: {permission: "Owner"}}};
});
diff --git a/routes/auth.js b/routes/auth.js
index e9e0ccea..7bdd96c2 100644
--- a/routes/auth.js
+++ b/routes/auth.js
@@ -1,21 +1,19 @@
var express = require('express');
var router = express.Router();
var _ = require('lodash');
-var security = require('../core/utils/security');
-var accountManager = require('../core/services/account-manager')();
-var AppError = require('../core/app-error');
+var config = require('../core/config');
+var validator = require('validator');
+var log4js = require('log4js');
+var log = log4js.getLogger("cps:auth");
+
+router.get('/password', (req, res) => {
+ res.render('auth/password', { title: 'CodePushServer' });
+});
router.get('/login', (req, res) => {
- var config = require('../core/config');
var codePushWebUrl = _.get(config, 'common.codePushWebUrl');
- var isRedirect = false;
- if (codePushWebUrl) {
- var validator = require('validator');
- if (validator.isURL(codePushWebUrl)){
- isRedirect = true;
- }
- }
- if (isRedirect) {
+ if (codePushWebUrl && validator.isURL(codePushWebUrl)) {
+ log.debug(`login redirect:${codePushWebUrl}`);
res.redirect(`${codePushWebUrl}/login`);
} else {
res.render('auth/login', { title: 'CodePushServer' });
@@ -27,16 +25,10 @@ router.get('/link', (req, res) => {
});
router.get('/register', (req, res) => {
- var config = require('../core/config');
var codePushWebUrl = _.get(config, 'common.codePushWebUrl');
var isRedirect = false;
- if (codePushWebUrl) {
- var validator = require('validator');
- if (validator.isURL(codePushWebUrl)){
- isRedirect = true;
- }
- }
- if (isRedirect) {
+ if (codePushWebUrl && validator.isURL(codePushWebUrl)) {
+ log.debug(`register redirect:${codePushWebUrl}`);
res.redirect(`${codePushWebUrl}/register`);
} else {
res.render('auth/login', { title: 'CodePushServer' });
@@ -48,20 +40,25 @@ router.post('/logout', (req, res) => {
});
router.post('/login', (req, res, next) => {
+ var AppError = require('../core/app-error');
+ var accountManager = require('../core/services/account-manager')();
+ var security = require('../core/utils/security');
var account = _.trim(req.body.account);
var password = _.trim(req.body.password);
- var config = require('../core/config');
var tokenSecret = _.get(config, 'jwt.tokenSecret');
+ log.debug(`login:${account}`);
accountManager.login(account, password)
.then((users) => {
var jwt = require('jsonwebtoken');
return jwt.sign({ uid: users.id, hash: security.md5(users.ack_code), expiredIn: 7200 }, tokenSecret);
})
.then((token) => {
+ log.debug(token);
res.send({status:'OK', results: {tokens: token}});
})
.catch((e) => {
if (e instanceof AppError.AppError) {
+ log.debug(e);
res.send({status:'ERROR', errorMessage: e.message});
} else {
next(e);
diff --git a/routes/index.js b/routes/index.js
index 9b1eb15c..d070b551 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -5,6 +5,8 @@ var AppError = require('../core/app-error');
var middleware = require('../core/middleware');
var ClientManager = require('../core/services/client-manager');
var _ = require('lodash');
+var log4js = require('log4js');
+var log = log4js.getLogger("cps:index");
router.get('/', (req, res, next) => {
res.render('index', { title: 'CodePushServer' });
@@ -39,9 +41,24 @@ router.get('/updateCheck', (req, res, next) => {
var appVersion = _.get(req, "query.appVersion");
var label = _.get(req, "query.label");
var packageHash = _.get(req, "query.packageHash")
+ var clientUniqueId = _.get(req, "query.clientUniqueId")
var clientManager = new ClientManager();
- clientManager.updateCheckFromCache(deploymentKey, appVersion, label, packageHash)
+ log.debug('req.query', req.query);
+ clientManager.updateCheckFromCache(deploymentKey, appVersion, label, packageHash, clientUniqueId)
.then((rs) => {
+ //灰度检测
+ return clientManager.chosenMan(rs.packageId, rs.rollout, clientUniqueId)
+ .then((data)=>{
+ if (!data) {
+ rs.isAvailable = false;
+ return rs;
+ }
+ return rs;
+ });
+ })
+ .then((rs) => {
+ delete rs.packageId;
+ delete rs.rollout;
res.send({"updateInfo":rs});
})
.catch((e) => {
@@ -54,6 +71,7 @@ router.get('/updateCheck', (req, res, next) => {
});
router.post('/reportStatus/download', (req, res) => {
+ log.debug('req.body', req.body);
var clientUniqueId = _.get(req, "body.clientUniqueId");
var label = _.get(req, "body.label");
var deploymentKey = _.get(req, "body.deploymentKey");
@@ -68,6 +86,7 @@ router.post('/reportStatus/download', (req, res) => {
});
router.post('/reportStatus/deploy', (req, res) => {
+ log.debug('req.body', req.body);
var clientUniqueId = _.get(req, "body.clientUniqueId");
var label = _.get(req, "body.label");
var deploymentKey = _.get(req, "body.deploymentKey");
diff --git a/routes/indexV1.js b/routes/indexV1.js
new file mode 100644
index 00000000..1720f1a8
--- /dev/null
+++ b/routes/indexV1.js
@@ -0,0 +1,89 @@
+var express = require('express');
+var router = express.Router();
+var Promise = require('bluebird');
+var AppError = require('../core/app-error');
+var middleware = require('../core/middleware');
+var ClientManager = require('../core/services/client-manager');
+var _ = require('lodash');
+var log4js = require('log4js');
+var log = log4js.getLogger("cps:indexV1");
+
+router.get('/update_check', (req, res, next) => {
+ var deploymentKey = _.get(req, "query.deployment_key");
+ var appVersion = _.get(req, "query.app_version");
+ var label = _.get(req, "query.label");
+ var packageHash = _.get(req, "query.package_hash")
+ var isCompanion = _.get(req, "query.is_companion")
+ var clientUniqueId = _.get(req, "query.client_unique_id")
+ var clientManager = new ClientManager();
+ log.debug('req.query', req.query);
+ clientManager.updateCheckFromCache(deploymentKey, appVersion, label, packageHash, clientUniqueId)
+ .then((rs) => {
+ //灰度检测
+ return clientManager.chosenMan(rs.packageId, rs.rollout, clientUniqueId)
+ .then((data)=>{
+ if (!data) {
+ rs.isAvailable = false;
+ return rs;
+ }
+ return rs;
+ });
+ })
+ .then((rs) => {
+ delete rs.packageId;
+ delete rs.rollout;
+ var update_info = {
+ download_url : rs.downloadUrl,
+ description : rs.description,
+ is_available : rs.isAvailable,
+ is_disabled : rs.isDisabled,
+ target_binary_range: rs.targetBinaryRange,
+ label: rs.label,
+ package_hash: rs.packageHash,
+ package_size: rs.packageSize,
+ should_run_binary_version: rs.shouldRunBinaryVersion,
+ update_app_version: rs.updateAppVersion,
+ is_mandatory: rs.isMandatory,
+ };
+ res.send({"update_info": update_info});
+ })
+ .catch((e) => {
+ if (e instanceof AppError.AppError) {
+ res.status(404).send(e.message);
+ } else {
+ next(e);
+ }
+ });
+});
+
+router.post('/report_status/download', (req, res) => {
+ log.debug('req.body', req.body);
+ var clientUniqueId = _.get(req, "body.client_unique_id");
+ var label = _.get(req, "body.label");
+ var deploymentKey = _.get(req, "body.deployment_key");
+ var clientManager = new ClientManager();
+ clientManager.reportStatusDownload(deploymentKey, label, clientUniqueId)
+ .catch((err) => {
+ if (!err instanceof AppError.AppError) {
+ console.error(err.stack)
+ }
+ });
+ res.send('OK');
+});
+
+router.post('/report_status/deploy', (req, res) => {
+ log.debug('req.body', req.body);
+ var clientUniqueId = _.get(req, "body.client_unique_id");
+ var label = _.get(req, "body.label");
+ var deploymentKey = _.get(req, "body.deployment_key");
+ var clientManager = new ClientManager();
+ clientManager.reportStatusDeploy(deploymentKey, label, clientUniqueId, req.body)
+ .catch((err) => {
+ if (!err instanceof AppError.AppError) {
+ console.error(err.stack)
+ }
+ });
+ res.send('OK');
+});
+
+module.exports = router;
diff --git a/routes/sessions.js b/routes/sessions.js
deleted file mode 100644
index a7b4ce18..00000000
--- a/routes/sessions.js
+++ /dev/null
@@ -1,24 +0,0 @@
-var express = require('express');
-var router = express.Router();
-var _ = require('lodash');
-var models = require('../models');
-var middleware = require('../core/middleware');
-var AppError = require('../core/app-error');
-
-router.delete('/:machineName', middleware.checkToken, (req, res, next) => {
- var machineName = _.trim(decodeURI(req.params.machineName));
- var uid = req.users.id;
- models.UserTokens.destroy({where: {created_by:machineName, uid: uid}})
- .then((rowNum) => {
- res.send("");
- })
- .catch(e => {
- if (e instanceof AppError.AppError) {
- res.status(406).send(e.message);
- } else {
- next(e);
- }
- });
-});
-
-module.exports = router;
diff --git a/sql/codepush-all.sql b/sql/codepush-all.sql
index e20dfe06..7e2d8776 100644
--- a/sql/codepush-all.sql
+++ b/sql/codepush-all.sql
@@ -1,17 +1,18 @@
-DROP TABLE IF EXISTS `apps`;
-CREATE TABLE `apps` (
+CREATE TABLE IF NOT EXISTS `apps` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL DEFAULT '',
`uid` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `os` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `platform` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `is_use_diff_text` tinyint(3) unsigned NOT NULL DEFAULT '0',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_name` (`name`(12))
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `collaborators`;
-CREATE TABLE `collaborators` (
+CREATE TABLE IF NOT EXISTS `collaborators` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`appid` int(10) unsigned NOT NULL DEFAULT '0',
`uid` bigint(20) unsigned NOT NULL DEFAULT '0',
@@ -24,9 +25,7 @@ CREATE TABLE `collaborators` (
KEY `idx_uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-DROP TABLE IF EXISTS `deployments`;
-CREATE TABLE `deployments` (
+CREATE TABLE IF NOT EXISTS `deployments` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`appid` int(10) unsigned NOT NULL DEFAULT '0',
`name` varchar(20) NOT NULL DEFAULT '',
@@ -42,8 +41,7 @@ CREATE TABLE `deployments` (
KEY `idx_deploymentkey` (`deployment_key`(40))
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `deployments_history`;
-CREATE TABLE `deployments_history` (
+CREATE TABLE IF NOT EXISTS `deployments_history` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`deployment_id` int(11) unsigned NOT NULL DEFAULT '0',
`package_id` int(10) unsigned NOT NULL DEFAULT '0',
@@ -53,22 +51,23 @@ CREATE TABLE `deployments_history` (
KEY `idx_deployment_id` (`deployment_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-DROP TABLE IF EXISTS `deployments_versions`;
-CREATE TABLE `deployments_versions` (
+CREATE TABLE IF NOT EXISTS `deployments_versions` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`deployment_id` int(11) unsigned NOT NULL DEFAULT '0',
- `app_version` varchar(14) NOT NULL DEFAULT '',
+ `app_version` varchar(100) NOT NULL DEFAULT '',
`current_package_id` int(10) unsigned NOT NULL DEFAULT '0',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
+ `min_version` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `max_version` bigint(20) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
- KEY `idx_did_appversion` (`deployment_id`,`app_version`)
+ KEY `idx_did_minversion` (`deployment_id`,`min_version`),
+ KEY `idx_did_maxversion` (`deployment_id`,`max_version`),
+ KEY `idx_did_appversion` (`deployment_id`,`app_version`(30))
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `packages`;
-CREATE TABLE `packages` (
+CREATE TABLE IF NOT EXISTS `packages` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`deployment_version_id` int(10) unsigned NOT NULL DEFAULT '0',
`deployment_id` int(10) unsigned NOT NULL DEFAULT '0',
@@ -83,16 +82,17 @@ CREATE TABLE `packages` (
`original_deployment` varchar(20) NOT NULL DEFAULT '',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_at` timestamp NULL DEFAULT NULL,
- `released_by` bigint(20) unsigned NOT NULL,
+ `released_by` bigint(20) unsigned NOT NULL DEFAULT '0',
`is_mandatory` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `is_disabled` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rollout` tinyint(3) unsigned NOT NULL DEFAULT '0',
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_deploymentid_label` (`deployment_id`,`label`(8)),
KEY `idx_versions_id` (`deployment_version_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `packages_diff`;
-CREATE TABLE `packages_diff` (
+CREATE TABLE IF NOT EXISTS `packages_diff` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`package_id` int(11) unsigned NOT NULL DEFAULT '0',
`diff_against_package_hash` varchar(64) NOT NULL DEFAULT '',
@@ -105,8 +105,7 @@ CREATE TABLE `packages_diff` (
KEY `idx_packageid_hash` (`package_id`,`diff_against_package_hash`(40))
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `packages_metrics`;
-CREATE TABLE `packages_metrics` (
+CREATE TABLE IF NOT EXISTS `packages_metrics` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`package_id` int(10) unsigned NOT NULL DEFAULT '0',
`active` int(10) unsigned NOT NULL DEFAULT '0',
@@ -120,8 +119,7 @@ CREATE TABLE `packages_metrics` (
KEY `idx_packageid` (`package_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `user_tokens`;
-CREATE TABLE `user_tokens` (
+CREATE TABLE IF NOT EXISTS `user_tokens` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`uid` bigint(20) unsigned NOT NULL DEFAULT '0',
`name` varchar(50) NOT NULL DEFAULT '',
@@ -137,8 +135,7 @@ CREATE TABLE `user_tokens` (
KEY `idx_tokens` (`tokens`) KEY_BLOCK_SIZE=16
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-DROP TABLE IF EXISTS `users`;
-CREATE TABLE `users` (
+CREATE TABLE IF NOT EXISTS `users` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL DEFAULT '',
`password` varchar(255) NOT NULL DEFAULT '',
@@ -157,9 +154,7 @@ INSERT INTO `users` (`id`, `username`, `password`, `email`, `identical`, `ack_co
VALUES
(1,'admin','$2a$12$mvUY9kTqW4kSoGuZFDW0sOSgKmNY8SPHVyVrSckBTLtXKf6vKX3W.','lisong2010@gmail.com','4ksvOXqog','oZmGE','2016-11-14 10:46:55','2016-02-29 21:24:49');
-
-DROP TABLE IF EXISTS `versions`;
-CREATE TABLE `versions` (
+CREATE TABLE IF NOT EXISTS `versions` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '1.DBversion',
`version` varchar(10) NOT NULL DEFAULT '',
@@ -170,5 +165,24 @@ CREATE TABLE `versions` (
LOCK TABLES `versions` WRITE;
INSERT INTO `versions` (`id`, `type`, `version`)
VALUES
- (1,1,'0.2.15');
-UNLOCK TABLES;
\ No newline at end of file
+ (1,1,'0.5.0');
+UNLOCK TABLES;
+
+CREATE TABLE IF NOT EXISTS `log_report_deploy` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `status` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `package_id` int(10) unsigned NOT NULL DEFAULT '0',
+ `client_unique_id` varchar(100) NOT NULL DEFAULT '',
+ `previous_label` varchar(20) NOT NULL DEFAULT '',
+ `previous_deployment_key` varchar(64) NOT NULL DEFAULT '',
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `log_report_download` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `package_id` int(10) unsigned NOT NULL DEFAULT '0',
+ `client_unique_id` varchar(100) NOT NULL DEFAULT '',
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
diff --git a/sql/codepush-v0.2.14.sql b/sql/codepush-v0.2.14-patch.sql
similarity index 100%
rename from sql/codepush-v0.2.14.sql
rename to sql/codepush-v0.2.14-patch.sql
diff --git a/sql/codepush-v0.2.15.sql b/sql/codepush-v0.2.15-patch.sql
similarity index 100%
rename from sql/codepush-v0.2.15.sql
rename to sql/codepush-v0.2.15-patch.sql
diff --git a/sql/codepush-v0.3.0-patch.sql b/sql/codepush-v0.3.0-patch.sql
new file mode 100644
index 00000000..bd672fe5
--- /dev/null
+++ b/sql/codepush-v0.3.0-patch.sql
@@ -0,0 +1,25 @@
+ALTER TABLE `apps` ADD `os` TINYINT UNSIGNED NOT NULL DEFAULT 0;
+ALTER TABLE `apps` ADD `platform` TINYINT UNSIGNED NOT NULL DEFAULT 0;
+ALTER TABLE `packages` ADD `is_disabled` TINYINT UNSIGNED NOT NULL DEFAULT 0;
+ALTER TABLE `packages` ADD `rollout` TINYINT UNSIGNED NOT NULL DEFAULT 100;
+
+CREATE TABLE `log_report_deploy` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `status` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `package_id` int(10) unsigned NOT NULL DEFAULT '0',
+ `client_unique_id` varchar(100) NOT NULL DEFAULT '',
+ `previous_label` varchar(20) NOT NULL DEFAULT '',
+ `previous_deployment_key` varchar(64) NOT NULL DEFAULT '',
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE `log_report_download` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `package_id` int(10) unsigned NOT NULL DEFAULT '0',
+ `client_unique_id` varchar(100) NOT NULL DEFAULT '',
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+UPDATE `versions` SET `version` = '0.3.0' WHERE `type` = '1';
\ No newline at end of file
diff --git a/sql/codepush-v0.4.0-patch.sql b/sql/codepush-v0.4.0-patch.sql
new file mode 100644
index 00000000..a8933f94
--- /dev/null
+++ b/sql/codepush-v0.4.0-patch.sql
@@ -0,0 +1,9 @@
+ALTER TABLE `deployments_versions` ADD `min_version` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0';
+ALTER TABLE `deployments_versions` ADD `max_version` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0';
+ALTER TABLE `deployments_versions` ADD INDEX `idx_did_min_version` (`deployment_id`, `min_version`);
+ALTER TABLE `deployments_versions` ADD INDEX `idx_did_maxversion` (`deployment_id`, `max_version`);
+ALTER TABLE `deployments_versions` CHANGE `app_version` `app_version` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '';
+ALTER TABLE `deployments_versions` DROP INDEX `idx_did_appversion`;
+ALTER TABLE `deployments_versions` ADD INDEX `idx_did_appversion` (`deployment_id`, `app_version` (30));
+
+UPDATE `versions` SET `version` = '0.4.0' WHERE `type` = '1';
\ No newline at end of file
diff --git a/sql/codepush-v0.5.0-patch.sql b/sql/codepush-v0.5.0-patch.sql
new file mode 100644
index 00000000..09b822d3
--- /dev/null
+++ b/sql/codepush-v0.5.0-patch.sql
@@ -0,0 +1,3 @@
+ALTER TABLE `apps` ADD `is_use_diff_text` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0';
+
+UPDATE `versions` SET `version` = '0.5.0' WHERE `type` = '1';
\ No newline at end of file
diff --git a/test/api/accessKeys/accessKeys.test.js b/test/api/accessKeys/accessKeys.test.js
index 83122f8d..69dc2fd6 100644
--- a/test/api/accessKeys/accessKeys.test.js
+++ b/test/api/accessKeys/accessKeys.test.js
@@ -30,14 +30,14 @@ describe('api/accessKeys/accessKeys.test.js', function() {
it('should create accessKeys successful', function(done) {
request.post(`/accessKeys`)
.set('Authorization', `Basic ${authToken}`)
- .send({createdBy: 'tablee', friendlyName: friendlyName, isSession: false, ttl: 30*24*60*60})
+ .send({createdBy: 'tablee', friendlyName: friendlyName, ttl: 30*24*60*60})
.end(function(err, res) {
should.not.exist(err);
res.status.should.equal(200);
var rs = JSON.parse(res.text);
rs.should.have.properties('accessKey');
rs.accessKey.should.have.properties(['name', 'createdTime', 'createdBy',
- 'expires', 'isSession', 'description', 'friendlyName']);
+ 'expires', 'description', 'friendlyName']);
done();
});
});
@@ -45,7 +45,7 @@ describe('api/accessKeys/accessKeys.test.js', function() {
it('should not create accessKeys successful when friendlyName exist', function(done) {
request.post(`/accessKeys`)
.set('Authorization', `Basic ${authToken}`)
- .send({createdBy: 'tablee', friendlyName: friendlyName, isSession: true, ttl: 30*24*60*60})
+ .send({createdBy: 'tablee', friendlyName: friendlyName, ttl: 30*24*60*60})
.end(function(err, res) {
should.not.exist(err);
res.status.should.equal(406);
@@ -68,84 +68,13 @@ describe('api/accessKeys/accessKeys.test.js', function() {
rs.accessKeys.should.be.an.instanceOf(Array);
rs.accessKeys.should.matchEach(function(it) {
return it.should.have.properties(['name', 'createdTime', 'createdBy',
- 'expires', 'isSession', 'description', 'friendlyName']);
+ 'expires', 'description', 'friendlyName']);
});
done();
});
});
});
- describe('modify accessKeys', function(done) {
- it('should modify accessKeys add ttl successful', function(done) {
- request.patch(`/accessKeys/${encodeURI(friendlyName)}`)
- .set('Authorization', `Basic ${authToken}`)
- .send({ttl: 7*24*60*60*1000})
- .end(function(err, res) {
- should.not.exist(err);
- res.status.should.equal(200);
- var rs = JSON.parse(res.text);
- rs.should.have.properties('accessKey');
- rs.accessKey.should.have.properties(['name', 'createdTime', 'createdBy',
- 'expires', 'isSession', 'description', 'friendlyName']);
- done();
- });
- });
-
- it('should modify accessKeys substact ttl successful', function(done) {
- request.patch(`/accessKeys/${encodeURI(friendlyName)}`)
- .set('Authorization', `Basic ${authToken}`)
- .send({ttl: -7*24*60*60*1000})
- .end(function(err, res) {
- should.not.exist(err);
- res.status.should.equal(200);
- var rs = JSON.parse(res.text);
- rs.should.have.properties('accessKey');
- rs.accessKey.should.have.properties(['name', 'createdTime', 'createdBy',
- 'expires', 'isSession', 'description', 'friendlyName']);
- done();
- });
- });
-
- it('should not modify accessKeys friendlyName successful when friendlyName exists', function(done) {
- request.patch(`/accessKeys/${encodeURI(friendlyName)}`)
- .set('Authorization', `Basic ${authToken}`)
- .send({friendlyName: friendlyName})
- .end(function(err, res) {
- should.not.exist(err);
- res.status.should.equal(406);
- res.text.should.equal(`The access key "${friendlyName}" already exists.`);
- done();
- });
- });
-
- it('should not modify accessKeys friendlyName successful when friendlyName invalid', function(done) {
- request.patch(`/accessKeys/${encodeURI(newFriendlyName)}`)
- .set('Authorization', `Basic ${authToken}`)
- .send({friendlyName: newFriendlyName})
- .end(function(err, res) {
- should.not.exist(err);
- res.status.should.equal(406);
- res.text.should.equal(`The access key "${newFriendlyName}" does not exist.`);
- done();
- });
- });
-
- it('should modify accessKeys friendlyName successful', function(done) {
- request.patch(`/accessKeys/${encodeURI(friendlyName)}`)
- .set('Authorization', `Basic ${authToken}`)
- .send({friendlyName: newFriendlyName})
- .end(function(err, res) {
- should.not.exist(err);
- res.status.should.equal(200);
- var rs = JSON.parse(res.text);
- rs.should.have.properties('accessKey');
- rs.accessKey.should.have.properties(['name', 'createdTime', 'createdBy',
- 'expires', 'isSession', 'description', 'friendlyName']);
- done();
- });
- });
- });
-
describe('delete accessKeys', function(done) {
it('should delete accessKeys successful', function(done) {
request.delete(`/accessKeys/${encodeURI(newFriendlyName)}`)
diff --git a/test/api/account/account.test.js b/test/api/account/account.test.js
index 83ebb2ce..62e262a3 100644
--- a/test/api/account/account.test.js
+++ b/test/api/account/account.test.js
@@ -35,7 +35,7 @@ describe('api/account/account.test.js', function() {
res.status.should.equal(200);
var rs = JSON.parse(res.text);
rs.should.have.properties('account');
- rs.account.should.have.properties(['email', 'id', 'linkedProviders', 'name']);
+ rs.account.should.have.properties(['email', 'linkedProviders', 'name']);
done();
});
});
diff --git a/test/api/apps/apps.test.js b/test/api/apps/apps.test.js
index 4628b7a9..d393a42f 100644
--- a/test/api/apps/apps.test.js
+++ b/test/api/apps/apps.test.js
@@ -13,8 +13,8 @@ describe('api/apps/apps.test.js', function() {
var authToken;
var machineName = `Login-${Math.random()}`;
var friendlyName = `Login-${Math.random()}`;
- var appName = 'test-ios';
- var newAppName = 'newtest-ios';
+ var appName = 'test';
+ var newAppName = 'newtest';
var bearerToken;
var testDeployment = 'test';
var newTestDeployment = 'newtest';
@@ -38,14 +38,14 @@ describe('api/apps/apps.test.js', function() {
it('should create accessKeys successful', function(done) {
request.post(`/accessKeys`)
.set('Authorization', `Basic ${authToken}`)
- .send({createdBy: machineName, friendlyName: friendlyName, isSession: true, ttl: 30*24*60*60})
+ .send({createdBy: machineName, friendlyName: friendlyName, ttl: 30*24*60*60})
.end(function(err, res) {
should.not.exist(err);
res.status.should.equal(200);
var rs = JSON.parse(res.text);
rs.should.have.properties('accessKey');
rs.accessKey.should.have.properties(['name', 'createdTime', 'createdBy',
- 'expires', 'isSession', 'description', 'friendlyName']);
+ 'expires', 'description', 'friendlyName']);
bearerToken = _.get(rs, 'accessKey.name');
done();
});
@@ -65,23 +65,10 @@ describe('api/apps/apps.test.js', function() {
});
});
- it('should not add apps successful when appName is invalid', function(done) {
- var appName = 'test';
- request.post(`/apps`)
- .set('Authorization', `Bearer ${bearerToken}`)
- .send({name: appName})
- .end(function(err, res) {
- should.not.exist(err);
- res.status.should.equal(406);
- res.text.should.equal(`appName have to point -android or -ios suffix! eg. ${appName}-android ${appName}-ios`);
- done();
- });
- });
-
it('should add apps successful', function(done) {
request.post(`/apps`)
.set('Authorization', `Bearer ${bearerToken}`)
- .send({name: appName})
+ .send({name: appName, os:'iOS', platform:'React-Native'})
.end(function(err, res) {
should.not.exist(err);
res.status.should.equal(200);
@@ -95,7 +82,7 @@ describe('api/apps/apps.test.js', function() {
it('should not add apps successful when appName exists', function(done) {
request.post(`/apps`)
.set('Authorization', `Bearer ${bearerToken}`)
- .send({name: appName})
+ .send({name: appName, os:'iOS', platform:'React-Native'})
.end(function(err, res) {
should.not.exist(err);
res.status.should.equal(406);
@@ -429,19 +416,6 @@ describe('api/apps/apps.test.js', function() {
});
});
- it(`should not rename apps successful when new name invalid`, function(done) {
- var newAppName = 'newtest';
- request.patch(`/apps/${appName}`)
- .set('Authorization', `Bearer ${bearerToken}`)
- .send({name: newAppName})
- .end(function(err, res) {
- should.not.exist(err);
- res.status.should.equal(406);
- res.text.should.equal(`new appName have to point -ios suffix! eg. Demo-ios`);
- done();
- });
- });
-
it(`should rename apps successful`, function(done) {
request.patch(`/apps/${appName}`)
.set('Authorization', `Bearer ${bearerToken}`)
diff --git a/test/api/apps/release.test.js b/test/api/apps/release.test.js
index dd6bdd9c..00eb698d 100644
--- a/test/api/apps/release.test.js
+++ b/test/api/apps/release.test.js
@@ -35,14 +35,14 @@ describe('api/apps/release.test.js', function() {
it('should create accessKeys successful', function(done) {
request.post(`/accessKeys`)
.set('Authorization', `Basic ${authToken}`)
- .send({createdBy: machineName, friendlyName: friendlyName, isSession: true, ttl: 30*24*60*60})
+ .send({createdBy: machineName, friendlyName: friendlyName, ttl: 30*24*60*60})
.end(function(err, res) {
should.not.exist(err);
res.status.should.equal(200);
var rs = JSON.parse(res.text);
rs.should.have.properties('accessKey');
rs.accessKey.should.have.properties(['name', 'createdTime', 'createdBy',
- 'expires', 'isSession', 'description', 'friendlyName']);
+ 'expires', 'description', 'friendlyName']);
bearerToken = _.get(rs, 'accessKey.name');
done();
});
@@ -53,7 +53,7 @@ describe('api/apps/release.test.js', function() {
it('should add apps successful', function(done) {
request.post(`/apps`)
.set('Authorization', `Bearer ${bearerToken}`)
- .send({name: appName})
+ .send({name: appName, os:'iOS', platform:'React-Native'})
.end(function(err, res) {
should.not.exist(err);
res.status.should.equal(200);
@@ -99,7 +99,6 @@ describe('api/apps/release.test.js', function() {
.set('Authorization', `Bearer ${bearerToken}`)
.send()
.end(function(err, res) {
- should.not.exist(err);
res.status.should.equal(200);
done();
});
diff --git a/test/api/auth/auth.test.js b/test/api/auth/auth.test.js
index 06311a30..0263639a 100644
--- a/test/api/auth/auth.test.js
+++ b/test/api/auth/auth.test.js
@@ -10,7 +10,7 @@ describe('api/auth/test.js', function() {
describe('sign in view', function(done) {
it('should show sign in redirect view successful', function(done) {
- _.set(config, 'common.codePushWebUrl', 'http://localhost:3001')
+ _.set(config, 'common.codePushWebUrl', 'http://127.0.0.1:3001')
request.get('/auth/login')
.send()
.end(function(err, res) {
@@ -34,7 +34,7 @@ describe('api/auth/test.js', function() {
describe('sign up view', function(done) {
it('should show sign up redirect view successful', function(done) {
- _.set(config, 'common.codePushWebUrl', 'http://localhost:3001')
+ _.set(config, 'common.codePushWebUrl', 'http://127.0.0.1:3001')
request.get('/auth/register')
.send()
.end(function(err, res) {
diff --git a/test/api/index/index.test.js b/test/api/index/index.test.js
index 0f8576a6..51f51289 100644
--- a/test/api/index/index.test.js
+++ b/test/api/index/index.test.js
@@ -117,7 +117,7 @@ describe('api/index/index.test.js', function() {
.end(function(err, res) {
should.not.exist(err);
res.status.should.equal(404);
- res.text.should.equal(`does not found deployment`);
+ res.text.should.equal(`Not found deployment, check deployment key is right.`);
done();
});
});
diff --git a/test/api/init/database.js b/test/api/init/database.js
index 90b7238e..a32c0aea 100644
--- a/test/api/init/database.js
+++ b/test/api/init/database.js
@@ -1,5 +1,5 @@
var config = require('../../../core/config');
-var mysql = require('mysql');
+var mysql = require('mysql2');
var redis = require('redis');
var should = require('should');
var fs = require('fs');
@@ -12,10 +12,11 @@ describe('api/init/database.js', function() {
var connection = mysql.createConnection({
host: config.db.host,
user: config.db.username,
- password: config.db.password
+ password: config.db.password,
+ multipleStatements: true
});
connection.connect();
- connection.query(`CREATE DATABASE IF NOT EXISTS ${config.db.database}`, function(err, rows, fields) {
+ connection.query(`DROP DATABASE IF EXISTS ${config.db.database};CREATE DATABASE IF NOT EXISTS ${config.db.database}`, function(err, rows, fields) {
should.not.exist(err);
done();
});
diff --git a/test/api/sessions/sessions.test.js b/test/api/sessions/sessions.test.js
deleted file mode 100644
index 3e7821a6..00000000
--- a/test/api/sessions/sessions.test.js
+++ /dev/null
@@ -1,58 +0,0 @@
-var app = require('../../../app');
-var request = require('supertest')(app);
-var should = require("should");
-var security = require('../../../core/utils/security');
-var factory = require('../../../core/utils/factory');
-var _ = require('lodash');
-
-describe('api/sessions/sessions.test.js', function() {
- var account = '522539441@qq.com';
- var password = '123456';
- var authToken;
- var friendlyName = 'sessions';
- var machineName = 'tablee.hosts';
- before(function(done){
- request.post('/auth/login')
- .send({
- account: account,
- password: password
- })
- .end(function(err, res) {
- should.not.exist(err);
- var rs = JSON.parse(res.text);
- rs.should.containEql({status:"OK"});
- authToken = (new Buffer(`auth:${_.get(rs, 'results.tokens')}`)).toString('base64');
- done();
- });
- });
-
- describe('create accessKeys', function() {
- it('should create accessKeys successful', function(done) {
- request.post(`/accessKeys`)
- .set('Authorization', `Basic ${authToken}`)
- .send({createdBy: machineName, friendlyName: friendlyName, isSession: true, ttl: 30*24*60*60})
- .end(function(err, res) {
- should.not.exist(err);
- res.status.should.equal(200);
- var rs = JSON.parse(res.text);
- rs.should.have.properties('accessKey');
- rs.accessKey.should.have.properties(['name', 'createdTime', 'createdBy',
- 'expires', 'isSession', 'description', 'friendlyName']);
- done();
- });
- });
- });
-
- describe('delete sessions', function() {
- it('should delete sessions successful', function(done) {
- request.delete(`/sessions/${encodeURI(machineName)}`)
- .set('Authorization', `Basic ${authToken}`)
- .send()
- .end(function(err, res) {
- should.not.exist(err);
- res.status.should.equal(200);
- done();
- });
- });
- });
-});
diff --git a/test/api/users/users.test.js b/test/api/users/users.test.js
index 57d87109..2b936dbf 100644
--- a/test/api/users/users.test.js
+++ b/test/api/users/users.test.js
@@ -78,7 +78,8 @@ describe('api/users/users.test.js', function() {
.then(function (t) {
storageToken = t;
done();
- });
+ })
+ .finally(() => client.quit());
});
it('should not check register code successful when email already exists', function(done) {
@@ -131,7 +132,8 @@ describe('api/users/users.test.js', function() {
.then(function (t) {
storageToken = t;
done();
- });
+ })
+ .finally(() => client.quit());
});
it('should not sign up successful when password length invalid', function(done) {
@@ -174,10 +176,13 @@ describe('api/users/users.test.js', function() {
it('should not change password successful when authToken invalid', function(done) {
request.patch(`/users/password`)
+ .set('Authorization', `Basic 11345`)
.send({oldPassword: password, newPassword: newPassword})
.end(function(err, res) {
should.not.exist(err);
- res.status.should.equal(401);
+ var rs = JSON.parse(res.text);
+ res.status.should.equal(200);
+ rs.should.containEql({status:401});
done();
});
});
diff --git a/views/auth/password.pug b/views/auth/password.pug
new file mode 100644
index 00000000..47fa302c
--- /dev/null
+++ b/views/auth/password.pug
@@ -0,0 +1,57 @@
+extends ../layout
+
+block content
+ .container(style="margin-top:30px;")
+ form#form.col-md-5.col-md-offset-3(method="post")
+ .form-group
+ label.sr-only(for="inputEmail") 邮箱地址/用户名
+ input#inputEmail.form-control(type="text" name="account" placeholder="邮箱地址/用户名" required autofocus)
+ .form-group
+ .col-md-10(style="margin-left:-15px;")
+ label.sr-only(for="inputToken") token
+ input#inputToken.form-control(type="text" name="token" placeholder="token" required)
+ .col-md-2
+ a.form-control.btn.btn-link(style="margin-bottom:15px;" target="_blank" href="/auth/login") 获取token
+ .form-group
+ label.sr-only(for="inputPassword") 原密码
+ input#inputPassword.form-control(type="password" name="oldPassword" placeholder="原密码" required)
+ .form-group
+ label.sr-only(for="inputNewPassword") 新密码
+ input#inputNewPassword.form-control(type="password" name="newPassword" placeholder="新密码" required)
+ .form-group
+ a#submitBtn.btn.btn-lg.btn-primary.btn-block 修改密码
+
+block js
+ script().
+ var submit = false;
+ $('#submitBtn').on('click', function () {
+ if (submit) {
+ return ;
+ }
+ var token = $('#inputToken').val();
+ var oldPassword = $('#inputPassword').val();
+ var newPassword = $('#inputNewPassword').val();
+ submit = true;
+ $.ajax({
+ type: 'patch',
+ data: JSON.stringify({oldPassword:oldPassword,newPassword:newPassword}),
+ contentType: 'application/json;charset=utf-8',
+ headers: {
+ Authorization : 'Bearer '+token
+ },
+ url: '/users/password',
+ dataType: 'json',
+ success: function (data) {
+ if (data.status == "OK") {
+ alert("修改成功");
+ location.href = '/auth/login';
+ } else if (data.status == 401) {
+ alert('token invalid');
+ } else {
+ alert(data.message);
+ }
+ submit = false;
+ }
+ });
+ });
+
diff --git a/views/index.pug b/views/index.pug
index c97cb43a..800da9bd 100644
--- a/views/index.pug
+++ b/views/index.pug
@@ -7,4 +7,5 @@ block content
h1(style="text-align: center;")= title
p(style="text-align: center;") Welcome to #{title}
.site-notice
- a.btn.btn-primary(href="/auth/login" type="button") 登录
\ No newline at end of file
+ a.btn.btn-primary(href="/auth/login" type="button") 登录
+ a.btn.btn-primary.col-md-offset-1(href="/auth/password" type="button") 修改密码
\ No newline at end of file
diff --git a/views/layout.pug b/views/layout.pug
index a0ba1682..85d88861 100644
--- a/views/layout.pug
+++ b/views/layout.pug
@@ -2,6 +2,8 @@ doctype html
html
head
title= title
+ meta(name="keywords" content="code-push-server,code-push,react-native,cordova")
+ meta(name="description" content="CodePush service is hotupdate services which adapter react-native-code-push and cordova-plugin-code-push")
link(rel='stylesheet', href='/js/bootstrap-3.3.7/css/bootstrap.min.css')
block css
body
diff --git a/views/tokens.pug b/views/tokens.pug
index 81829751..ad770b56 100644
--- a/views/tokens.pug
+++ b/views/tokens.pug
@@ -53,7 +53,10 @@ block js
$.ajax({
type: 'post',
data: postParams,
- url: '/accessKeys?access_token='+access_token,
+ headers: {
+ Authorization : 'Bearer '+access_token
+ },
+ url: '/accessKeys',
dataType: 'json',
success: function (data) {
submit = false;