Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af724960c2 | ||
|
|
79797376d3 | ||
|
|
7fd264440e | ||
|
|
1982b9404e | ||
|
|
4bea6f4e5f | ||
|
|
3dd04b6aac | ||
|
|
ec605efc9c | ||
|
|
7f27447dac | ||
|
|
a2c151f246 | ||
|
|
35efc132b3 | ||
|
|
dd37527651 | ||
|
|
a82e772097 | ||
|
|
70fb543e7a | ||
|
|
bbfc3e57db | ||
|
|
9b9ec50ccf | ||
|
|
4098d966b8 | ||
|
|
6e7d528c9f | ||
|
|
701abe831f | ||
|
|
449deab2ed | ||
|
|
20f1ab3af8 | ||
|
|
a932b6487d | ||
|
|
8f05a6e8b9 | ||
|
|
4a91795288 | ||
|
|
de72396cd2 | ||
|
|
7f48d45e0d | ||
|
|
553c1daf85 | ||
|
|
084b83e8d7 | ||
|
|
05e5ac86ba | ||
|
|
2a530a3d6e | ||
|
|
bde9de9f90 | ||
|
|
d538658be9 | ||
|
|
81a010bc53 | ||
|
|
7f2c5725e5 | ||
|
|
7f89b3be92 | ||
|
|
1c6eb50e05 | ||
|
|
aa8d4ac850 | ||
|
|
e0eeefa43e | ||
|
|
662b91ea9a | ||
|
|
cc70f9e6b1 | ||
|
|
7312d761ca |
@@ -2,3 +2,6 @@ node_modules/
|
||||
coverage/
|
||||
.tmp/
|
||||
.git/
|
||||
tools/
|
||||
public/
|
||||
test/
|
||||
|
||||
10
.travis.yml
10
.travis.yml
@@ -1,8 +1,12 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- 'iojs-1.2'
|
||||
- '0.12'
|
||||
- '1'
|
||||
- '2'
|
||||
- '3'
|
||||
- '4'
|
||||
addons:
|
||||
- postgresql: '9.3'
|
||||
script: 'make test-travis-all'
|
||||
after_script: 'npm install coveralls@2 && cat ./coverage/lcov.info | coveralls'
|
||||
after_script:
|
||||
- "npm i codecov.io && cat ./coverage/coverage.json | ./node_modules/codecov.io/bin/codecov.io.js"
|
||||
|
||||
49
History.md
49
History.md
@@ -1,4 +1,53 @@
|
||||
|
||||
2.2.0 / 2015-09-29
|
||||
==================
|
||||
|
||||
* feat: list packages by username
|
||||
* test: use codecov
|
||||
* feat(badge): support custom subject
|
||||
* fix(sync): add recover logic
|
||||
* feat(sync): add sync scripts
|
||||
|
||||
2.1.5 / 2015-09-05
|
||||
==================
|
||||
|
||||
* fix: only sync update packages
|
||||
|
||||
2.1.4 / 2015-09-05
|
||||
==================
|
||||
|
||||
* fix: support new array and old map format both
|
||||
* fix: /-/all/since had been redirect to /-/all/static/today.json
|
||||
* fix(list): let koa-etag to caculate the etag
|
||||
|
||||
2.1.3 / 2015-08-18
|
||||
==================
|
||||
|
||||
* fix: sync public scope package download url is wrong
|
||||
* fix: default registry change to taobao registry
|
||||
|
||||
2.1.2 / 2015-08-09
|
||||
==================
|
||||
|
||||
* fix(syncer): sync worker pkg null bug
|
||||
* feat(web): add downloads badge
|
||||
|
||||
2.1.1 / 2015-07-27
|
||||
==================
|
||||
|
||||
* fix: fix private scope package detect
|
||||
* fix: dont sync if upstream is npm registry
|
||||
* fix(sync): support sync public scope package
|
||||
* test: fix fails tests
|
||||
* fix: ignore 503 server error
|
||||
* fix: ignore sync 503 server error
|
||||
|
||||
2.1.0 / 2015-07-08
|
||||
==================
|
||||
|
||||
* feat(web): search support jsonp
|
||||
* fix function name
|
||||
|
||||
2.0.0 / 2015-05-11
|
||||
==================
|
||||
|
||||
|
||||
5
Makefile
5
Makefile
@@ -3,12 +3,12 @@ REPORTER = spec
|
||||
TIMEOUT = 30000
|
||||
MOCHA_OPTS =
|
||||
DB = sqlite
|
||||
DISTURL = http://npm.taobao.org/mirrors/iojs
|
||||
DISTURL = https://npm.taobao.org/mirrors/iojs
|
||||
BIN = iojs
|
||||
|
||||
ifeq ($(findstring io.js, $(shell which node)),)
|
||||
BIN = node
|
||||
DISTURL = http://npm.taobao.org/mirrors/node
|
||||
DISTURL = https://npm.taobao.org/mirrors/node
|
||||
endif
|
||||
|
||||
install:
|
||||
@@ -80,7 +80,6 @@ test-travis: install init-database
|
||||
$(BIN) --harmony \
|
||||
node_modules/.bin/istanbul cover --preserve-comments \
|
||||
node_modules/.bin/_mocha \
|
||||
--report lcovonly \
|
||||
-- -u exports \
|
||||
--reporter dot \
|
||||
--timeout $(TIMEOUT) \
|
||||
|
||||
19
README.md
19
README.md
@@ -3,29 +3,20 @@ cnpmjs.org
|
||||
|
||||
[![NPM version][npm-image]][npm-url]
|
||||
[![build status][travis-image]][travis-url]
|
||||
[![Test coverage][coveralls-image]][coveralls-url]
|
||||
[![Gittip][gittip-image]][gittip-url]
|
||||
[![Test coverage][cov-image]][cov-url]
|
||||
[![David deps][david-image]][david-url]
|
||||
[![node version][node-image]][node-url]
|
||||
[![npm download][download-image]][download-url]
|
||||
[![gitter][gitter-image]][gitter-url]
|
||||
|
||||
[npm-image]: http://cnpmjs.org/badge/v/cnpmjs.org.svg?style=flat-square
|
||||
[npm-url]: http://cnpmjs.org/package/cnpmjs.org
|
||||
[travis-image]: https://img.shields.io/travis/cnpm/cnpmjs.org.svg?style=flat-square
|
||||
[travis-url]: https://travis-ci.org/cnpm/cnpmjs.org
|
||||
[coveralls-image]: https://img.shields.io/coveralls/cnpm/cnpmjs.org.svg?style=flat-square
|
||||
[coveralls-url]: https://coveralls.io/r/cnpm/cnpmjs.org?branch=master
|
||||
[gittip-image]: https://img.shields.io/gittip/fengmk2.svg?style=flat-square
|
||||
[gittip-url]: https://www.gittip.com/fengmk2/
|
||||
[cov-image]: http://codecov.io/github/cnpm/cnpmjs.org/coverage.svg?branch=master
|
||||
[cov-url]: http://codecov.io/github/cnpm/cnpmjs.org?branch=master
|
||||
[david-image]: https://img.shields.io/david/cnpm/cnpmjs.org.svg?style=flat-square
|
||||
[david-url]: https://david-dm.org/cnpm/cnpmjs.org
|
||||
[node-image]: https://img.shields.io/badge/node.js-%3E=_0.11-red.svg?style=flat-square
|
||||
[node-url]: http://nodejs.org/download/
|
||||
[download-image]: https://img.shields.io/npm/dm/cnpmjs.org.svg?style=flat-square
|
||||
[download-url]: https://npmjs.org/package/cnpmjs.org
|
||||
[gitter-image]: https://badges.gitter.im/Join%20Chat.svg
|
||||
[gitter-url]: https://gitter.im/cnpm/cnpmjs.org?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
|
||||
|
||||

|
||||
|
||||
@@ -120,8 +111,8 @@ Tips: make sure your code is following the [node-style-guide](https://github.com
|
||||
|
||||
## Sponsors
|
||||
|
||||
- [](http://www.ucloud.cn/sdk?sem=sdk-CNPMJS)
|
||||
- [](http://www.ucloud.cn?sem=sdk-CNPMJS)
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
[MIT](LICENSE.txt)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
/**!
|
||||
* cnpmjs.org - config/index.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
@@ -161,7 +159,7 @@ var config = {
|
||||
enablePrivate: false,
|
||||
|
||||
// registry scopes, if don't set, means do not support scopes
|
||||
scopes: [ '@cnpm', '@cnpmtest' ],
|
||||
scopes: [ '@cnpm', '@cnpmtest', '@cnpm-test' ],
|
||||
|
||||
// some registry already have some private packages in global scope
|
||||
// but we want to treat them as scoped private packages,
|
||||
|
||||
@@ -45,9 +45,6 @@ module.exports = function* list() {
|
||||
}
|
||||
}
|
||||
|
||||
// use modifiedTime as etag
|
||||
this.set('ETag', '"' + modifiedTime.getTime() + '"');
|
||||
|
||||
// must set status first
|
||||
this.status = 200;
|
||||
if (this.fresh) {
|
||||
|
||||
29
controllers/registry/package/list_by_user.js
Normal file
29
controllers/registry/package/list_by_user.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/**!
|
||||
* list packages by username
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <m@fengmk2.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
const packageService = require('../../../services/package');
|
||||
|
||||
module.exports = function*() {
|
||||
const username = this.params.user;
|
||||
const packages = yield packageService.listModulesByUser(username);
|
||||
|
||||
this.body = {
|
||||
user: {
|
||||
name: username,
|
||||
},
|
||||
packages: packages,
|
||||
};
|
||||
};
|
||||
@@ -21,7 +21,7 @@ var config = require('../config');
|
||||
|
||||
exports.sync = function* () {
|
||||
var username = this.user.name || 'anonymous';
|
||||
var name = this.params.name;
|
||||
var name = this.params.name || this.params[0];
|
||||
var type = 'package';
|
||||
if (name.indexOf(':') > 0) {
|
||||
// user:fengmk2
|
||||
@@ -60,7 +60,7 @@ exports.sync = function* () {
|
||||
};
|
||||
|
||||
exports.getSyncLog = function* (next) {
|
||||
var logId = this.params.id;
|
||||
var logId = this.params.id || this.params[1];
|
||||
var offset = Number(this.query.offset) || 0;
|
||||
var row = yield* Log.get(logId);
|
||||
if (!row) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
@@ -196,6 +196,11 @@ SyncModuleWorker.prototype._doneOne = function* (concurrencyId, name, success) {
|
||||
};
|
||||
|
||||
SyncModuleWorker.prototype.syncUpstream = function* (name) {
|
||||
if (config.sourceNpmRegistry.indexOf('registry.npmjs.org') >= 0 ||
|
||||
config.sourceNpmRegistry.indexOf('registry.npmjs.com') >= 0) {
|
||||
this.log('----------------- upstream is npm registry: %s, ignore it -------------------', config.sourceNpmRegistry);
|
||||
return;
|
||||
}
|
||||
var syncname = name;
|
||||
if (this.type === 'user') {
|
||||
syncname = this.type + ':' + syncname;
|
||||
@@ -302,17 +307,17 @@ SyncModuleWorker.prototype.next = function* (concurrencyId) {
|
||||
|
||||
this.log('----------------- Syncing %s -------------------', name);
|
||||
|
||||
// ignore scoped package
|
||||
if (name[0] === '@') {
|
||||
this.log('[c#%d] [%s] ignore sync scoped package',
|
||||
concurrencyId, name);
|
||||
// ignore private scoped package
|
||||
if (common.isPrivateScopedPackage(name)) {
|
||||
this.log('[c#%d] [%s] ignore sync private scoped %j package',
|
||||
concurrencyId, name, config.scopes);
|
||||
yield* this._doneOne(concurrencyId, name, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// get from npm
|
||||
try {
|
||||
var result = yield* npmSerivce.request('/' + name);
|
||||
var result = yield* npmSerivce.request('/' + name.replace('/', '%2f'));
|
||||
pkg = result.data;
|
||||
status = result.status;
|
||||
} catch (err) {
|
||||
@@ -329,7 +334,7 @@ SyncModuleWorker.prototype.next = function* (concurrencyId) {
|
||||
var unpublishedInfo = null;
|
||||
if (status === 404) {
|
||||
// check if it's unpublished
|
||||
if (pkg.time && pkg.time.unpublished && pkg.time.unpublished.time) {
|
||||
if (pkg && pkg.time && pkg.time.unpublished && pkg.time.unpublished.time) {
|
||||
unpublishedInfo = pkg.time.unpublished;
|
||||
} else {
|
||||
pkg = null;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -18,6 +18,7 @@ var utility = require('utility');
|
||||
var util = require('util');
|
||||
var config = require('../../config');
|
||||
var packageService = require('../../services/package');
|
||||
var DownloadTotal = require('../../services/download_total');
|
||||
|
||||
exports.version = function* () {
|
||||
var color = 'lightgrey';
|
||||
@@ -40,9 +41,22 @@ exports.version = function* () {
|
||||
}
|
||||
|
||||
var subject = config.badgeSubject.replace(/\-/g, '--');
|
||||
if (this.query.subject) {
|
||||
subject = this.query.subject.replace(/\-/g, '--');
|
||||
}
|
||||
version = version.replace(/\-/g, '--');
|
||||
var style = this.query.style || 'flat-square';
|
||||
var url = util.format('https://img.shields.io/badge/%s-%s-%s.svg?style=%s',
|
||||
subject, version, color, utility.encodeURIComponent(style));
|
||||
utility.encodeURIComponent(subject), version, color, utility.encodeURIComponent(style));
|
||||
this.redirect(url);
|
||||
};
|
||||
|
||||
exports.downloads = function* () {
|
||||
// https://img.shields.io/badge/downloads-100k/month-brightgreen.svg?style=flat-square
|
||||
var name = this.params[0];
|
||||
var count = yield DownloadTotal.getTotalByName(name);
|
||||
var style = this.query.style || 'flat-square';
|
||||
var url = util.format('https://img.shields.io/badge/downloads-%s-brightgreen.svg?style=%s',
|
||||
count, utility.encodeURIComponent(style));
|
||||
this.redirect(url);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* fengmk2 <m@fengmk2.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -35,13 +35,12 @@ module.exports = function* search() {
|
||||
|
||||
// return a json result
|
||||
if (this.query && this.query.type === 'json') {
|
||||
this.body = {
|
||||
this.jsonp = {
|
||||
keyword: word,
|
||||
match: match,
|
||||
packages: result.searchMatchs,
|
||||
keywords: result.keywordMatchs,
|
||||
};
|
||||
this.type = 'application/json; charset=utf-8';
|
||||
return;
|
||||
}
|
||||
yield this.render('search', {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
/**!
|
||||
* cnpmjs.org - dispatch.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com>
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -52,36 +52,42 @@ So `cnpm` is meaning: **Company npm**.
|
||||
|
||||
<script src="/js/readme.js"></script>
|
||||
|
||||
## Version Badge
|
||||
## Badges
|
||||
|
||||
Default style is `flat-square`.
|
||||
|
||||
Badge URL: `https://cnpmjs.org/badge/v/cnpmjs.org.svg` 
|
||||
### Version
|
||||
|
||||
Badge URL: `http://cnpmjs.org/badge/v/cnpmjs.org.svg` 
|
||||
|
||||
* `<0.1.0 & >=0.0.0`: 
|
||||
* `<1.0.0 & >=0.1.0`: 
|
||||
* `>=1.0.0`: 
|
||||
|
||||
### Downloads
|
||||
|
||||
Badge URL: `http://cnpmjs.org/badge/d/cnpmjs.org.svg` 
|
||||
|
||||
## Usage
|
||||
|
||||
use our npm client [cnpm](https://github.com/cnpm/cnpm)(More suitable with cnpmjs.org and gzip support), you can get our client through npm:
|
||||
|
||||
```bash
|
||||
$ npm install -g cnpm --registry=https://r.cnpmjs.org
|
||||
$ npm install -g cnpm --registry=http://registry.npm.taobao.org
|
||||
```
|
||||
|
||||
Or you can alias NPM to use it:
|
||||
|
||||
```bash
|
||||
alias cnpm="npm --registry=https://r.cnpmjs.org \
|
||||
alias cnpm="npm --registry=http://registry.npm.taobao.org \
|
||||
--cache=$HOME/.npm/.cache/cnpm \
|
||||
--disturl=https://cnpmjs.org/mirrors/node \
|
||||
--disturl=http://registry.npm.taobao.org/mirrors/node \
|
||||
--userconfig=$HOME/.cnpmrc"
|
||||
|
||||
#Or alias it in .bashrc or .zshrc
|
||||
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=https://r.cnpmjs.org \
|
||||
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=http://registry.npm.taobao.org \
|
||||
--cache=$HOME/.npm/.cache/cnpm \
|
||||
--disturl=https://cnpmjs.org/mirrors/node \
|
||||
--disturl=http://registry.npm.taobao.org/mirrors/node \
|
||||
--userconfig=$HOME/.cnpmrc"' >> ~/.zshrc && source ~/.zshrc
|
||||
```
|
||||
|
||||
@@ -104,7 +110,7 @@ $ cnpm sync connect
|
||||
sync package on web: [sync/connect](/sync/connect)
|
||||
|
||||
```bash
|
||||
$ open https://cnpmjs.org/sync/connect
|
||||
$ open http://registry.npm.taobao.org/sync/connect
|
||||
```
|
||||
|
||||
### publish / unpublish
|
||||
@@ -138,4 +144,4 @@ Release [History](/history).
|
||||
|
||||
## Sponsors
|
||||
|
||||
- [](http://www.ucloud.cn/sdk?sem=sdk-CNPMJS)
|
||||
- [](http://www.ucloud.cn?sem=sdk-CNPMJS)
|
||||
|
||||
2
index.js
2
index.js
@@ -1,6 +1,4 @@
|
||||
/**!
|
||||
* cnpmjs.org - index.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
|
||||
@@ -27,6 +27,11 @@ exports.getTarballFilepath = function (filename) {
|
||||
};
|
||||
|
||||
exports.getCDNKey = function (name, filename) {
|
||||
// if name is scope package name, need to auto fix filename as a scope package file name
|
||||
// e.g.: @scope/foo, filename: foo-1.0.0.tgz => filename: @scope/foo-1.0.0.tgz
|
||||
if (name[0] === '@' && filename[0] !== '@') {
|
||||
filename = name.split('/')[0] + '/' + filename;
|
||||
}
|
||||
return '/' + name + '/-/' + filename;
|
||||
};
|
||||
|
||||
@@ -66,3 +71,10 @@ exports.isLocalModule = function (mods) {
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
exports.isPrivateScopedPackage = function (name) {
|
||||
if (name[0] !== '@') {
|
||||
return false;
|
||||
}
|
||||
return config.scopes.indexOf(name.split('/')[0]) >= 0;
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@ var template = '<?xml version="1.0" encoding="UTF-8"?>\
|
||||
<Url method="get" type="text/html" template="http://${host}/browse/keyword/{searchTerms}"/>\
|
||||
</OpenSearchDescription>';
|
||||
|
||||
module.exports = function *publishable(next) {
|
||||
module.exports = function *opensearch(next) {
|
||||
if (this.path === '/opensearch.xml') {
|
||||
this.type = 'text/xml';
|
||||
this.charset = 'utf-8';
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
/**!
|
||||
* cnpmjs.org - models/index.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -15,7 +13,6 @@
|
||||
*/
|
||||
|
||||
var path = require('path');
|
||||
var Sequelize = require('sequelize');
|
||||
var sequelize = require('../common/sequelize');
|
||||
|
||||
function load(name) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cnpmjs.org",
|
||||
"version": "2.0.0",
|
||||
"version": "2.2.0",
|
||||
"description": "Private npm registry and web for Enterprise, base on MySQL and Simple Store Service",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
@@ -38,6 +38,7 @@
|
||||
"koa-markdown": "~2.0.1",
|
||||
"koa-middlewares": "~2.1.0",
|
||||
"koa-mock": "~1.1.4",
|
||||
"koa-safe-jsonp": "~0.3.0",
|
||||
"markdown-it": "~3.0.6",
|
||||
"mime": "~1.3.4",
|
||||
"mini-logger": "~1.0.0",
|
||||
@@ -90,7 +91,7 @@
|
||||
"registry"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 0.11.14"
|
||||
"node": ">= 1.0.0"
|
||||
},
|
||||
"author": [
|
||||
"fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)",
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
/**!
|
||||
* cnpmjs.org - routes/registry.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com>
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -36,6 +34,7 @@ var removeOneVersion = require('../controllers/registry/package/remove_version')
|
||||
var updatePackage = require('../controllers/registry/package/update');
|
||||
var downloadPackage = require('../controllers/registry/package/download');
|
||||
var downloadTotal = require('../controllers/registry/package/download_total');
|
||||
var listPackagesByUser = require('../controllers/registry/package/list_by_user');
|
||||
|
||||
var addUser = require('../controllers/registry/user/add');
|
||||
var showUser = require('../controllers/registry/user/show');
|
||||
@@ -77,7 +76,9 @@ function routes(app) {
|
||||
app.put('/:name', login, publishable, savePackage);
|
||||
|
||||
// sync from source npm
|
||||
app.put(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/sync$/, sync.sync);
|
||||
app.put('/:name/sync', sync.sync);
|
||||
app.get(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/sync\/log\/(\d+)$/, sync.getSyncLog);
|
||||
app.get('/:name/sync/log/:id', sync.getSyncLog);
|
||||
|
||||
// add tag
|
||||
@@ -111,6 +112,7 @@ function routes(app) {
|
||||
|
||||
// list all packages of user
|
||||
app.get('/-/by-user/:user', userPackage.list);
|
||||
app.get('/-/users/:user/packages', listPackagesByUser);
|
||||
|
||||
// download times
|
||||
app.get('/downloads/range/:range/:name', downloadTotal);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com>
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* fengmk2 <m@fengmk2.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -42,15 +42,19 @@ function routes(app) {
|
||||
|
||||
app.get('/~:name', showUser);
|
||||
|
||||
app.get(/\/sync\/(@[\w\-\.]+\/[\w\-\.]+)$/, showSync);
|
||||
app.get('/sync/:name', showSync);
|
||||
app.get('/sync', showSync);
|
||||
app.put(/\/sync\/(@[\w\-\.]+\/[\w\-\.]+)$/, sync.sync);
|
||||
app.put('/sync/:name', sync.sync);
|
||||
|
||||
app.get(/\/sync\/(@[\w\-\.]+\/[\w\-\.]+)\/log\/(\d+)$/, sync.getSyncLog);
|
||||
app.get('/sync/:name/log/:id', sync.getSyncLog);
|
||||
|
||||
app.get('/_list/search/search', searchRange);
|
||||
|
||||
app.get(/^\/badge\/v\/([@\w\-\.\/]+)\.svg$/, badge.version);
|
||||
app.get(/^\/badge\/d\/([@\w\-\.\/]+)\.svg$/, badge.downloads);
|
||||
}
|
||||
|
||||
module.exports = routes;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com>
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* fengmk2 <m@fengmk2.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -28,6 +28,7 @@ var auth = require('../middleware/auth');
|
||||
var proxyToNpm = require('../middleware/proxy_to_npm');
|
||||
var routes = require('../routes/web');
|
||||
var config = require('../config');
|
||||
var jsonp = require('koa-safe-jsonp');
|
||||
var path = require('path');
|
||||
var http = require('http');
|
||||
var koa = require('koa');
|
||||
@@ -35,6 +36,8 @@ var fs = require('fs');
|
||||
|
||||
var app = koa();
|
||||
|
||||
jsonp(app);
|
||||
|
||||
var rootdir = path.dirname(__dirname);
|
||||
|
||||
app.use(block());
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
|
||||
var config = require('../config');
|
||||
var isPrivateScopedPackage = require('../lib/common').isPrivateScopedPackage;
|
||||
|
||||
config.privatePackages = config.privatePackages || [];
|
||||
|
||||
exports.isPrivatePackage = function* (name) {
|
||||
if (name[0] === '@') {
|
||||
exports.isPrivatePackage = function (name) {
|
||||
if (isPrivateScopedPackage(name)) {
|
||||
return true;
|
||||
}
|
||||
if (config.privatePackages.indexOf(name) >= 0) {
|
||||
|
||||
@@ -32,6 +32,27 @@ exports.getModuleTotal = function* (name, start, end) {
|
||||
return formatRows(rows, start, end);
|
||||
};
|
||||
|
||||
exports.getTotalByName = function* (name) {
|
||||
var rows = yield DownloadTotal.findAll({
|
||||
where: {
|
||||
name: name
|
||||
}
|
||||
});
|
||||
var count = 0;
|
||||
rows.forEach(function (row) {
|
||||
for (var i = 1; i <= 31; i++) {
|
||||
var day = i < 10 ? '0' + i : String(i);
|
||||
var field = 'd' + day;
|
||||
var val = row[field];
|
||||
if (typeof val === 'string') {
|
||||
val = utility.toSafeNumber(val);
|
||||
}
|
||||
count += val;
|
||||
}
|
||||
});
|
||||
return count;
|
||||
};
|
||||
|
||||
exports.plusModuleTotal = function* (data) {
|
||||
var yearMonth = parseYearMonth(data.date);
|
||||
// all module download total
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var ms = require('humanize-ms');
|
||||
var urllib = require('../common/urllib');
|
||||
var config = require('../config');
|
||||
|
||||
@@ -39,7 +40,8 @@ function* request(url, options) {
|
||||
var data = err.data || '[empty]';
|
||||
if (err.name === 'JSONResponseFormatError' && statusCode >= 500) {
|
||||
err.name = 'NPMServerError';
|
||||
err.message = 'Status ' + statusCode + ', ' + data.toString();
|
||||
err.status = statusCode;
|
||||
err.message = 'Url: ' + url + ', Status ' + statusCode + ', ' + data.toString();
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
@@ -68,6 +70,83 @@ exports.get = function* (name) {
|
||||
return data;
|
||||
};
|
||||
|
||||
exports.fetchUpdatesSince = function* (lastSyncTime, timeout) {
|
||||
var lastModified = lastSyncTime - ms('10m');
|
||||
var data = yield exports.getAllSince(lastModified, timeout);
|
||||
var result = {
|
||||
lastModified: lastSyncTime,
|
||||
names: [],
|
||||
};
|
||||
if (!data) {
|
||||
return result;
|
||||
}
|
||||
if (Array.isArray(data)) {
|
||||
// support https://registry.npmjs.org/-/all/static/today.json
|
||||
var maxModified;
|
||||
data.forEach(function (pkg) {
|
||||
if (pkg.time && pkg.time.modified) {
|
||||
var modified = Date.parse(pkg.time.modified);
|
||||
if (modified >= lastModified) {
|
||||
result.names.push(pkg.name);
|
||||
}
|
||||
if (!maxModified || modified > maxModified) {
|
||||
maxModified = modified;
|
||||
}
|
||||
} else {
|
||||
result.names.push(pkg.name);
|
||||
}
|
||||
});
|
||||
if (maxModified) {
|
||||
result.lastModified = maxModified;
|
||||
}
|
||||
} else {
|
||||
// /-/all/since
|
||||
if (data._updated) {
|
||||
result.lastModified = data._updated;
|
||||
delete data._updated;
|
||||
}
|
||||
result.names = Object.keys(data);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
exports.fetchAllPackagesSince = function* (timestamp) {
|
||||
var r = yield request('/-/all/static/all.json', {
|
||||
registry: 'http://registry.npmjs.org',
|
||||
timeout: 600000
|
||||
});
|
||||
// {"_updated":1441520402174,"0":{"name":"0","dist-tags
|
||||
// "time":{"modified":"2014-06-17T06:38:43.495Z"}
|
||||
var data = r.data;
|
||||
var result = {
|
||||
lastModified: timestamp,
|
||||
lastModifiedName: null,
|
||||
names: [],
|
||||
};
|
||||
var maxModified;
|
||||
for (var key in data) {
|
||||
if (key === '_updated') {
|
||||
continue;
|
||||
}
|
||||
var pkg = data[key];
|
||||
if (!pkg.time || !pkg.time.modified) {
|
||||
continue;
|
||||
}
|
||||
var modified = Date.parse(pkg.time.modified);
|
||||
if (modified >= timestamp) {
|
||||
result.names.push(pkg.name);
|
||||
}
|
||||
if (!maxModified || modified > maxModified) {
|
||||
maxModified = modified;
|
||||
result.lastModifiedName = pkg.name;
|
||||
}
|
||||
}
|
||||
if (maxModified) {
|
||||
result.lastModified = maxModified;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
exports.getAllSince = function* (startkey, timeout) {
|
||||
var r = yield* request('/-/all/since?stale=update_after&startkey=' + startkey, {
|
||||
timeout: timeout || 300000
|
||||
@@ -75,6 +154,14 @@ exports.getAllSince = function* (startkey, timeout) {
|
||||
return r.data;
|
||||
};
|
||||
|
||||
exports.getAllToday = function* (timeout) {
|
||||
var r = yield* request('/-/all/static/today.json', {
|
||||
timeout: timeout || 300000
|
||||
});
|
||||
// data is array: see https://registry.npmjs.org/-/all/static/today.json
|
||||
return r.data;
|
||||
};
|
||||
|
||||
exports.getShort = function* (timeout) {
|
||||
var r = yield* request('/-/short', {
|
||||
timeout: timeout || 300000,
|
||||
|
||||
@@ -128,7 +128,7 @@ exports.listModules = function* (names) {
|
||||
id: ids
|
||||
},
|
||||
attributes: [
|
||||
'name', 'description'
|
||||
'name', 'description', 'version',
|
||||
]
|
||||
});
|
||||
return rows;
|
||||
@@ -219,7 +219,7 @@ exports.listAllPublicModuleNames = function* () {
|
||||
var sql = 'SELECT DISTINCT(name) AS name FROM tag ORDER BY name';
|
||||
var rows = yield models.query(sql);
|
||||
return rows.filter(function (row) {
|
||||
return row.name[0] !== '@';
|
||||
return !common.isPrivatePackage(row.name);
|
||||
}).map(function (row) {
|
||||
return row.name;
|
||||
});
|
||||
@@ -500,8 +500,7 @@ exports.updatePrivateModuleMaintainers = function* (name, usernames) {
|
||||
};
|
||||
|
||||
function* getMaintainerModel(name) {
|
||||
var isPrivatePackage = yield* common.isPrivatePackage(name);
|
||||
return isPrivatePackage ? PrivateModuleMaintainer : NpmModuleMaintainer;
|
||||
return common.isPrivatePackage(name) ? PrivateModuleMaintainer : NpmModuleMaintainer;
|
||||
}
|
||||
|
||||
exports.listMaintainers = function* (name) {
|
||||
|
||||
@@ -142,6 +142,10 @@ function sendMailToAdmin(err, result, syncTime) {
|
||||
var type;
|
||||
var html;
|
||||
if (err) {
|
||||
// ignore 503 error
|
||||
if (err.status === 503) {
|
||||
return;
|
||||
}
|
||||
subject = 'Sync Error';
|
||||
type = 'error';
|
||||
html = util.format('Sync packages from official registry failed.\n' +
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - sync/sync_all.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -6,6 +6,7 @@
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
* fengmk2 <m@fengmk2.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -14,7 +15,6 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var ms = require('humanize-ms');
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var config = require('../config');
|
||||
var Status = require('./status');
|
||||
@@ -40,19 +40,6 @@ function* getFirstSyncPackages(lastSyncModule) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get all the packages that update time > lastSyncTime
|
||||
* @param {Number} lastSyncTime
|
||||
*/
|
||||
function* getCommonSyncPackages(lastSyncTime) {
|
||||
var data = yield* npmService.getAllSince(lastSyncTime);
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
delete data._updated;
|
||||
return Object.keys(data);
|
||||
}
|
||||
|
||||
module.exports = function* sync() {
|
||||
var syncTime = Date.now();
|
||||
var info = yield* totalService.getTotalInfo();
|
||||
@@ -66,7 +53,9 @@ module.exports = function* sync() {
|
||||
logger.syncInfo('First time sync all packages from official registry');
|
||||
packages = yield* getFirstSyncPackages(info.last_sync_module);
|
||||
} else {
|
||||
packages = yield* getCommonSyncPackages(info.last_sync_time - ms('10m'));
|
||||
var result = yield npmService.fetchUpdatesSince(info.last_sync_time);
|
||||
syncTime = result.lastModified;
|
||||
packages = result.names;
|
||||
}
|
||||
|
||||
packages = packages || [];
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
/*!
|
||||
* cnpmjs.org - sync/sync_exist.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
@@ -16,7 +14,6 @@
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:sync:sync_exist');
|
||||
var Status = require('./status');
|
||||
var ms = require('humanize-ms');
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var config = require('../config');
|
||||
var npmService = require('../services/npm');
|
||||
@@ -63,16 +60,10 @@ module.exports = function* sync() {
|
||||
}
|
||||
allPackages = pkgs;
|
||||
} else {
|
||||
debug('sync new module from last exist sync time: %s', info.last_sync_time);
|
||||
var data = yield* npmService.getAllSince(info.last_exist_sync_time - ms('10m'));
|
||||
if (!data) {
|
||||
allPackages = [];
|
||||
}
|
||||
if (data._updated) {
|
||||
syncTime = data._updated;
|
||||
delete data._updated;
|
||||
}
|
||||
allPackages = Object.keys(data);
|
||||
debug('sync new module from last exist sync time: %s', info.last_exist_sync_time);
|
||||
var result = yield npmService.fetchUpdatesSince(info.last_exist_sync_time);
|
||||
allPackages = result.names;
|
||||
syncTime = result.lastModified;
|
||||
}
|
||||
|
||||
var packages = intersection(existPackages, allPackages);
|
||||
|
||||
66
sync/sync_since.js
Normal file
66
sync/sync_since.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/**!
|
||||
* sync packages since by some days ago
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <m@fengmk2.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
const thunkify = require('thunkify-wrap');
|
||||
const co = require('co');
|
||||
const ms = require('humanize-ms');
|
||||
const npmService = require('../services/npm');
|
||||
const SyncModuleWorker = require('../controllers/sync_module_worker');
|
||||
|
||||
function* sync(sinceTimestamp) {
|
||||
console.log('Fetching packages since: %s', new Date(sinceTimestamp));
|
||||
var result = yield npmService.fetchAllPackagesSince(sinceTimestamp);
|
||||
var packages = result.names;
|
||||
|
||||
packages = packages || [];
|
||||
if (!packages.length) {
|
||||
console.log('no packages need be sync');
|
||||
process.exit(0);
|
||||
}
|
||||
// var news = [];
|
||||
// for (var i = 0; i < packages.length; i++) {
|
||||
// if (packages[i] === 'elwms') {
|
||||
// news = packages.slice(i);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// packages = news;
|
||||
console.log('lastModified: %s, lastModified package: %s, total %d packages to sync: %j',
|
||||
new Date(result.lastModified), result.lastModifiedName, packages.length, packages);
|
||||
|
||||
var worker = new SyncModuleWorker({
|
||||
username: 'sync_since',
|
||||
name: packages,
|
||||
noDep: true,
|
||||
concurrency: 1,
|
||||
syncUpstreamFirst: false,
|
||||
});
|
||||
worker.start();
|
||||
var end = thunkify.event(worker);
|
||||
yield end();
|
||||
|
||||
console.log('All packages sync done, successes %d, fails %d',
|
||||
worker.successes.length, worker.fails.length);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
co(function* () {
|
||||
var timestamp = Date.now() - ms(process.argv[2] || '30d');
|
||||
yield sync(timestamp);
|
||||
}).catch(function (err) {
|
||||
console.error(err.stack);
|
||||
process.exit(1);
|
||||
});
|
||||
87
test/controllers/registry/package/list_by_user.test.js
Normal file
87
test/controllers/registry/package/list_by_user.test.js
Normal file
@@ -0,0 +1,87 @@
|
||||
/**!
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var request = require('supertest');
|
||||
var mm = require('mm');
|
||||
var should = require('should');
|
||||
var app = require('../../../../servers/registry');
|
||||
var utils = require('../../../utils');
|
||||
|
||||
describe('test/controllers/registry/package/list_by_user.test.js', function () {
|
||||
var user = 'cnpmjstest_list_by_user';
|
||||
var userauth = 'Basic ' + new Buffer(user + ':' + user).toString('base64');
|
||||
|
||||
afterEach(mm.restore);
|
||||
|
||||
before(function (done) {
|
||||
var pkg = utils.getPackage('@cnpmtest/list_by_user_module1', '1.0.1', user);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', userauth)
|
||||
.send(pkg)
|
||||
.expect(201, function (err) {
|
||||
should.not.exist(err);
|
||||
|
||||
var pkg2 = utils.getPackage('@cnpmtest/list_by_user_module2', '2.0.0', user);
|
||||
request(app)
|
||||
.put('/' + pkg2.name)
|
||||
.set('authorization', userauth)
|
||||
.send(pkg2)
|
||||
.expect(201, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /-/users/:user/packages', function () {
|
||||
it('should get 200', function (done) {
|
||||
var url = '/-/users/' + user + '/packages';
|
||||
request(app)
|
||||
.get(url)
|
||||
.expect(function(res) {
|
||||
var data = res.body;
|
||||
data.user.name.should.equal(user);
|
||||
var map = {};
|
||||
data.packages.forEach(function(pkg) {
|
||||
map[pkg.name] = pkg;
|
||||
});
|
||||
map['@cnpmtest/list_by_user_module1'].should.be.an.Object;
|
||||
map['@cnpmtest/list_by_user_module1'].should.eql({
|
||||
name: '@cnpmtest/list_by_user_module1',
|
||||
description: '',
|
||||
version: '1.0.1',
|
||||
});
|
||||
|
||||
map['@cnpmtest/list_by_user_module2'].should.be.an.Object;
|
||||
map['@cnpmtest/list_by_user_module2'].should.eql({
|
||||
name: '@cnpmtest/list_by_user_module2',
|
||||
description: '',
|
||||
version: '2.0.0',
|
||||
});
|
||||
})
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it('should get empty packages list when user not exists', function (done) {
|
||||
var url = '/-/users/not-exist-username/packages';
|
||||
request(app)
|
||||
.get(url)
|
||||
.expect({
|
||||
user: {
|
||||
name: 'not-exist-username',
|
||||
},
|
||||
packages: [],
|
||||
})
|
||||
.expect(200, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,7 @@
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -23,7 +23,7 @@ var config = require('../../../../config');
|
||||
var packageService = require('../../../../services/package');
|
||||
var nfs = require('../../../../common/nfs');
|
||||
|
||||
describe('controllers/registry/package/remove.test.js', function () {
|
||||
describe('test/controllers/registry/package/remove.test.js', function () {
|
||||
afterEach(mm.restore);
|
||||
|
||||
before(function (done) {
|
||||
@@ -74,7 +74,7 @@ describe('controllers/registry/package/remove.test.js', function () {
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect({
|
||||
error: 'invalid scope',
|
||||
reason: 'scope @cnpm-not-exists not match legal scopes: @cnpm, @cnpmtest'
|
||||
reason: 'scope @cnpm-not-exists not match legal scopes: @cnpm, @cnpmtest, @cnpm-test'
|
||||
})
|
||||
.expect(400, done);
|
||||
});
|
||||
@@ -85,7 +85,7 @@ describe('controllers/registry/package/remove.test.js', function () {
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect({
|
||||
error: 'no_perms',
|
||||
reason: 'only allow publish with @cnpm, @cnpmtest scope(s)'
|
||||
reason: 'only allow publish with @cnpm, @cnpmtest, @cnpm-test scope(s)'
|
||||
})
|
||||
.expect(403, done);
|
||||
});
|
||||
|
||||
@@ -51,7 +51,7 @@ describe('controllers/registry/user/show.test.js, GET /-/user/org.couchdb.user:n
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.name.should.equal('fengmk2');
|
||||
res.body.github.should.equal('fengmk2');
|
||||
// res.body.github.should.equal('fengmk2');
|
||||
res.body._cnpm_meta.should.have.keys('id', 'npm_user', 'custom_user',
|
||||
'gmt_modified', 'gmt_create');
|
||||
res.body._cnpm_meta.npm_user.should.equal(true);
|
||||
|
||||
@@ -129,7 +129,7 @@ describe('controllers/sync.test.js', function () {
|
||||
it('should sync scope package not found', function (done) {
|
||||
request(webApp.listen())
|
||||
.put('/sync/@cnpm/not-exists-package')
|
||||
.expect(404, done);
|
||||
.expect(201, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -17,6 +17,7 @@
|
||||
var mm = require('mm');
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var request = require('supertest');
|
||||
var urllib = require('urllib');
|
||||
var config = require('../../config');
|
||||
var SyncModuleWorker = require('../../controllers/sync_module_worker');
|
||||
var logService = require('../../services/module_log');
|
||||
@@ -24,7 +25,7 @@ var packageService = require('../../services/package');
|
||||
var utils = require('../utils');
|
||||
var app = require('../../servers/registry');
|
||||
|
||||
describe('controllers/sync_module_worker.test.js', function () {
|
||||
describe('test/controllers/sync_module_worker.test.js', function () {
|
||||
afterEach(mm.restore);
|
||||
|
||||
beforeEach(function () {
|
||||
@@ -53,9 +54,9 @@ describe('controllers/sync_module_worker.test.js', function () {
|
||||
yield end();
|
||||
});
|
||||
|
||||
it('should not sync scoped package', function* () {
|
||||
it('should not sync private scoped package', function* () {
|
||||
var worker = new SyncModuleWorker({
|
||||
name: '@scoped/google',
|
||||
name: '@cnpmtest/google',
|
||||
username: 'fengmk2',
|
||||
});
|
||||
worker.start();
|
||||
@@ -63,6 +64,44 @@ describe('controllers/sync_module_worker.test.js', function () {
|
||||
yield end();
|
||||
});
|
||||
|
||||
it('should sync public scoped package', function* () {
|
||||
mm(config, 'sourceNpmRegistry', 'https://registry.npmjs.org');
|
||||
var worker = new SyncModuleWorker({
|
||||
name: '@sindresorhus/df',
|
||||
username: 'fengmk2',
|
||||
});
|
||||
worker.start();
|
||||
var end = thunkify.event(worker, 'end');
|
||||
yield end();
|
||||
|
||||
// sync again
|
||||
var worker = new SyncModuleWorker({
|
||||
name: '@sindresorhus/df',
|
||||
username: 'fengmk2',
|
||||
});
|
||||
worker.start();
|
||||
var end = thunkify.event(worker, 'end');
|
||||
yield end();
|
||||
|
||||
var tgzUrl;
|
||||
function checkResult() {
|
||||
return function (done) {
|
||||
request(app.listen())
|
||||
.get('/@sindresorhus/df')
|
||||
.expect(function (res) {
|
||||
var latest = res.body.versions[res.body['dist-tags']['latest']];
|
||||
tgzUrl = latest.dist.tarball;
|
||||
})
|
||||
.expect(200, done);
|
||||
};
|
||||
}
|
||||
|
||||
yield checkResult();
|
||||
|
||||
var r = yield urllib.request(tgzUrl);
|
||||
r.status.should.equal(200);
|
||||
});
|
||||
|
||||
it('should start a sync worker and dont sync deps', function* () {
|
||||
var log = yield* logService.create({
|
||||
name: 'byte',
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -55,6 +55,21 @@ describe('controllers/web/badge.test.js', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should support custom subject', function (done) {
|
||||
var pkg = utils.getPackage('@cnpmtest/badge-test-module', '3.0.1', utils.admin, 'v3');
|
||||
request(registry)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send(pkg)
|
||||
.end(function (err) {
|
||||
should.not.exists(err);
|
||||
request(app)
|
||||
.get('/badge/v/@cnpmtest/badge-test-module.svg?style=flat-square&tag=v3&subject=ant-design')
|
||||
.expect('Location', 'https://img.shields.io/badge/ant--design-3.0.1-blue.svg?style=flat-square')
|
||||
.expect(302, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should support 1.0.0-beta1', function (done) {
|
||||
var pkg = utils.getPackage('@cnpmtest/badge-test-module', '1.0.0-beta1', utils.admin);
|
||||
request(registry)
|
||||
@@ -107,4 +122,21 @@ describe('controllers/web/badge.test.js', function () {
|
||||
.expect(302, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /badge/d/:name.svg', function () {
|
||||
it('should show downloads count', function (done) {
|
||||
var pkg = utils.getPackage('@cnpmtest/badge-download-module', '1.0.1', utils.admin);
|
||||
request(registry)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send(pkg)
|
||||
.end(function (err) {
|
||||
should.not.exists(err);
|
||||
request(app)
|
||||
.get('/badge/d/@cnpmtest/badge-download-module.svg?style=flat-square')
|
||||
.expect('Location', 'https://img.shields.io/badge/downloads-0-brightgreen.svg?style=flat-square')
|
||||
.expect(302, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
* fengmk2 <m@fengmk2.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -49,9 +50,23 @@ describe('controllers/web/package/search.test.js', function () {
|
||||
request(app)
|
||||
.get('/browse/keyword/@cnpmtest/testmodule-web-search?type=json')
|
||||
.expect(200)
|
||||
.expect({
|
||||
keyword: '@cnpmtest/testmodule-web-search',
|
||||
match: { name: '@cnpmtest/testmodule-web-search', description: '' },
|
||||
packages: [ { name: '@cnpmtest/testmodule-web-search', description: '' } ],
|
||||
keywords: []
|
||||
})
|
||||
.expect('content-type', 'application/json; charset=utf-8', done);
|
||||
});
|
||||
|
||||
it('should search with jsonp work', function (done) {
|
||||
request(app)
|
||||
.get('/browse/keyword/@cnpmtest/testmodule-web-search?type=json&callback=foo')
|
||||
.expect(200)
|
||||
.expect('/**/ typeof foo === \'function\' && foo({"keyword":"@cnpmtest/testmodule-web-search","match":{"name":"@cnpmtest/testmodule-web-search","description":""},"packages":[{"name":"@cnpmtest/testmodule-web-search","description":""}],"keywords":[]});')
|
||||
.expect('content-type', 'application/javascript; charset=utf-8', done);
|
||||
});
|
||||
|
||||
it('should list no match ok', function (done) {
|
||||
request(app)
|
||||
.get('/browse/keyword/notexistpackage')
|
||||
|
||||
@@ -115,13 +115,13 @@ describe('controllers/web/package/show.test.js', function () {
|
||||
|
||||
describe('unpublished package', function () {
|
||||
before(function (done) {
|
||||
utils.sync('tnpm', done);
|
||||
utils.sync('tfs', done);
|
||||
});
|
||||
|
||||
it('should display unpublished info', function (done) {
|
||||
mm(config, 'syncModel', 'all');
|
||||
request(app)
|
||||
.get('/package/tnpm')
|
||||
.get('/package/tfs')
|
||||
.expect(200)
|
||||
.expect(/This package has been unpublished\./, done);
|
||||
});
|
||||
|
||||
@@ -38,6 +38,7 @@ var usernames = [
|
||||
'cnpmjstest10', // admin
|
||||
'cnpmjstestAdmin2', // other admin
|
||||
'cnpmjstestAdmin3', // other admin
|
||||
'cnpmjstest_list_by_user',
|
||||
];
|
||||
|
||||
var count = usernames.length;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com>
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
var common = require('../../lib/common');
|
||||
|
||||
describe('lib/common.test.js', function () {
|
||||
describe('test/lib/common.test.js', function () {
|
||||
describe('isAdmin()', function () {
|
||||
it('should admin is admin', function () {
|
||||
common.isAdmin('admin').should.equal(true);
|
||||
@@ -26,4 +26,13 @@ describe('lib/common.test.js', function () {
|
||||
common.isAdmin('toString').should.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCDNKey()', function () {
|
||||
it('should auto fix scope filename', function () {
|
||||
common.getCDNKey('foo', 'foo-1.0.0.tgz').should.equal('/foo/-/foo-1.0.0.tgz');
|
||||
common.getCDNKey('@bar/foo', 'foo-1.0.0.tgz').should.equal('/@bar/foo/-/@bar/foo-1.0.0.tgz');
|
||||
common.getCDNKey('@bar/foo', '@bar/foo-1.0.0.tgz').should.equal('/@bar/foo/-/@bar/foo-1.0.0.tgz');
|
||||
common.getCDNKey('@bar/foo', '@bar1/foo-1.0.0.tgz').should.equal('/@bar/foo/-/@bar1/foo-1.0.0.tgz');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,13 +24,14 @@ describe('services/common.test.js', function () {
|
||||
describe('isPrivatePackage()', function () {
|
||||
it('should detect prviate package', function* () {
|
||||
mm(config, 'privatePackages', ['some-private-package', 'foobar2']);
|
||||
(yield* common.isPrivatePackage('@cnpm/ooxx')).should.equal(true);
|
||||
(yield* common.isPrivatePackage('@cnpm/some-private-package')).should.equal(true);
|
||||
(yield* common.isPrivatePackage('some-private-package')).should.equal(true);
|
||||
(yield* common.isPrivatePackage('foobar2')).should.equal(true);
|
||||
common.isPrivatePackage('@cnpm/ooxx').should.equal(true);
|
||||
common.isPrivatePackage('@cnpm/some-private-package').should.equal(true);
|
||||
common.isPrivatePackage('some-private-package').should.equal(true);
|
||||
common.isPrivatePackage('foobar2').should.equal(true);
|
||||
|
||||
(yield* common.isPrivatePackage('foobar')).should.equal(false);
|
||||
(yield* common.isPrivatePackage('pedding-2')).should.equal(false);
|
||||
common.isPrivatePackage('foobar').should.equal(false);
|
||||
common.isPrivatePackage('pedding-2').should.equal(false);
|
||||
common.isPrivatePackage('@public/some-package').should.equal(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -98,9 +98,9 @@ describe('services/default_user_service.test.js', function () {
|
||||
login: 'fengmk2',
|
||||
email: 'fengmk2@gmail.com',
|
||||
name: 'Yuan Feng',
|
||||
html_url: 'http://fengmk2.github.com',
|
||||
html_url: 'http://cnpmjs.org/~fengmk2',
|
||||
avatar_url: 'https://secure.gravatar.com/avatar/95b9d41231617a05ced5604d242c9670?s=50&d=retro',
|
||||
im_url: 'https://twitter.com/fengmk2',
|
||||
im_url: '',
|
||||
site_admin: true,
|
||||
scopes: ['@cnpm', '@cnpmtest'],
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
var DownloadTotal = require('../../services/download_total');
|
||||
|
||||
describe('services/download_total.test.js', function () {
|
||||
describe('test/services/download_total.test.js', function () {
|
||||
describe('plusModuleTotal()', function () {
|
||||
it('should plus one module download count', function* () {
|
||||
var data = {
|
||||
@@ -89,4 +89,25 @@ describe('services/download_total.test.js', function () {
|
||||
rows[2].count.should.equal(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTotalByName()', function () {
|
||||
it('should get total downloads', function* () {
|
||||
var data = {
|
||||
date: '2014-10-21',
|
||||
name: 'getTotalByName-module',
|
||||
count: 1000
|
||||
};
|
||||
yield* DownloadTotal.plusModuleTotal(data);
|
||||
|
||||
data = {
|
||||
date: '2015-10-22',
|
||||
name: 'getTotalByName-module',
|
||||
count: 2
|
||||
};
|
||||
yield* DownloadTotal.plusModuleTotal(data);
|
||||
|
||||
var count = yield DownloadTotal.getTotalByName('getTotalByName-module');
|
||||
count.should.equal(1002);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,7 +55,8 @@ describe('services/npm.test.js', function () {
|
||||
throw new Error('should not run this');
|
||||
} catch (err) {
|
||||
err.name.should.equal('NPMServerError');
|
||||
err.message.should.equal('Status 500, ' + fs.readFileSync(path.join(fixtures, '500.txt'), 'utf8'));
|
||||
err.message.should.containEql('Status 500, ' +
|
||||
fs.readFileSync(path.join(fixtures, '500.txt'), 'utf8'));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -19,7 +19,7 @@ var sleep = require('co-sleep');
|
||||
var Package = require('../../services/package');
|
||||
var utils = require('../utils');
|
||||
|
||||
describe('services/package.test.js', function () {
|
||||
describe('test/services/package.test.js', function () {
|
||||
function* createModule(name, version, user, tag) {
|
||||
var sourcePackage = {
|
||||
version: version,
|
||||
@@ -124,7 +124,7 @@ describe('services/package.test.js', function () {
|
||||
var mods = yield* Package.listPublicModulesByUser('listPublicModuleNamesByUser-user');
|
||||
mods.should.length(3);
|
||||
mods.forEach(function (mod) {
|
||||
mod.toJSON().should.have.keys('name', 'description');
|
||||
mod.toJSON().should.have.keys('name', 'description', 'version');
|
||||
mod.name.should.containEql('listPublicModuleNamesByUser-module');
|
||||
});
|
||||
});
|
||||
@@ -160,12 +160,11 @@ describe('services/package.test.js', function () {
|
||||
});
|
||||
|
||||
it('should work', function* () {
|
||||
yield* createModule('@cnpm-test-scope1/test-listPrivateModules-module-1', '1.0.0');
|
||||
yield* createModule('@cnpm-test-scope1/test-listPrivateModules-module-2', '1.0.0');
|
||||
var modules = yield* Package.listPrivateModulesByScope('@cnpm-test-scope1');
|
||||
// console.log(modules[0].toJSON())
|
||||
yield* createModule('@cnpm-test/test-listPrivateModules-module-1', '1.0.0');
|
||||
yield* createModule('@cnpm-test/test-listPrivateModules-module-2', '1.0.0');
|
||||
var modules = yield* Package.listPrivateModulesByScope('@cnpm-test');
|
||||
modules.should.length(2);
|
||||
modules[0].name.should.containEql('@cnpm-test-scope1/test-listPrivateModules-module-');
|
||||
modules[0].name.should.containEql('@cnpm-test/test-listPrivateModules-module-');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ var npmSerivce = require('../../services/npm');
|
||||
var totalService = require('../../services/total');
|
||||
var packageService = require('../../services/package');
|
||||
|
||||
describe('sync/sync_all.test.js', function () {
|
||||
describe('test/sync/sync_all.test.js', function () {
|
||||
beforeEach(function () {
|
||||
mm(config, 'syncModel', 'all');
|
||||
});
|
||||
@@ -48,5 +48,31 @@ describe('sync/sync_all.test.js', function () {
|
||||
data.successes.should.eql(['mk2testmodule']);
|
||||
mm.restore();
|
||||
});
|
||||
|
||||
it('should sync with array data format ok', function* () {
|
||||
mm.data(npmSerivce, 'getAllSince', [
|
||||
{
|
||||
name: 'mk2testmodule',
|
||||
versions: {
|
||||
'0.0.2': 'latest'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'mk2testmodule1',
|
||||
time: {
|
||||
modified: '2015-09-05T07:31:35.734Z',
|
||||
},
|
||||
versions: {
|
||||
'0.0.2': 'latest'
|
||||
}
|
||||
},
|
||||
]);
|
||||
mm.data(npmSerivce, 'getShort', ['mk2testmodule']);
|
||||
mm.data(totalService, 'getTotalInfo', {last_sync_time: Date.now()});
|
||||
mm.data(packageService, 'listAllPublicModuleNames', [ 'mk2testmodule' ]);
|
||||
var data = yield sync;
|
||||
data.successes.should.eql(['mk2testmodule']);
|
||||
mm.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ var npmService = require('../../services/npm');
|
||||
var totalService = require('../../services/total');
|
||||
var utils = require('../utils');
|
||||
|
||||
describe('sync/sync_exist.test.js', function () {
|
||||
describe('test/sync/sync_exist.test.js', function () {
|
||||
beforeEach(function () {
|
||||
mm(config, 'syncModel', 'all');
|
||||
});
|
||||
@@ -43,15 +43,28 @@ describe('sync/sync_exist.test.js', function () {
|
||||
it('should sync common ok', function *() {
|
||||
mm.data(npmService, 'getAllSince', {
|
||||
_updated: Date.now(),
|
||||
'byte': {},
|
||||
byte: {},
|
||||
});
|
||||
mm.data(totalService, 'getTotalInfo', {last_exist_sync_time: Date.now()});
|
||||
var data = yield* sync();
|
||||
data.successes.should.eql(['byte']);
|
||||
|
||||
mm.data(npmService, 'getAllSince', {
|
||||
_updated: Date.now(),
|
||||
});
|
||||
mm.data(npmService, 'getAllSince', []);
|
||||
var data = yield* sync();
|
||||
data.successes.should.eql([]);
|
||||
});
|
||||
|
||||
it('should sync with array format data', function *() {
|
||||
mm.data(npmService, 'getAllSince', [
|
||||
{
|
||||
name: 'byte',
|
||||
}
|
||||
]);
|
||||
mm.data(totalService, 'getTotalInfo', {last_exist_sync_time: Date.now()});
|
||||
var data = yield* sync();
|
||||
data.successes.should.eql(['byte']);
|
||||
|
||||
mm.data(npmService, 'getAllSince', []);
|
||||
var data = yield* sync();
|
||||
data.successes.should.eql([]);
|
||||
});
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
/**!
|
||||
* cnpmjs.org - test/utils.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -59,10 +59,12 @@
|
||||
<% if (!packages.length) { %>
|
||||
<div class="alert alert-warning">Can not found any package by <%= user.name %>.</div>
|
||||
<% } %>
|
||||
<% for (var i = 0; i < packages.length; i++) { %>
|
||||
<% for (var i = 0; i < packages.length; i++) {
|
||||
var package = packages[i];
|
||||
%>
|
||||
<div class="package">
|
||||
<a href="/package/<%= packages[i].name %>" class="package-name"><%= packages[i].name %></a>
|
||||
<span class="package-description"><%= packages[i].description %></span>
|
||||
<a href="/package/<%= packages[i].name %>" class="package-name"><%= package.name %></a>
|
||||
<span class="package-description">(latest: <%= package.version %>) <%= package.description %></span>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,11 @@
|
||||
var $log = $('#sync-log');
|
||||
var $notify = $('#sync-notify');
|
||||
var timer;
|
||||
var name = '<%= type %>:<%= name %>';
|
||||
var type = '<%= type %>';
|
||||
var name = '<%= name %>';
|
||||
if (type !== 'package') {
|
||||
name = type + ':' + name;
|
||||
}
|
||||
var resourceURL = '/sync/' + name;
|
||||
$(function() {
|
||||
var checkLogId = location.hash.match(/logid=(\d+)/);
|
||||
|
||||
Reference in New Issue
Block a user