Compare commits

...

41 Commits
0.3.1 ... 0.3.6

Author SHA1 Message Date
fengmk2
70d12f235a Release 0.3.6 2014-03-06 15:05:07 +08:00
fengmk2
aa7077e4b0 install missing package should sync it from source npm. fixed #252 2014-03-06 15:04:07 +08:00
dead_horse
f5c19668f5 Merge pull request #254 from cnpm/jshint
Add jshint check: $ make jshint
2014-03-06 14:09:07 +08:00
fengmk2
2b2a1904c6 npm publish dont contains .jshint* 2014-03-06 14:02:33 +08:00
fengmk2
a234c7e289 npm test run jshint 2014-03-06 14:00:43 +08:00
fengmk2
63562c30ec Add jshint check: $ make jshint
* .jshint make esnext true
* .jshintignore ignore no need check dirs
2014-03-06 13:55:13 +08:00
dead_horse
6968331b38 Merge pull request #251 from cnpm/yield-next
use `yield* next` instead of `yield next`
2014-03-05 16:55:32 +08:00
fengmk2
fc1a17e4d1 use yield* next instead of yield next
* According to koajs/compose#2, show performances improve a lot.
2014-03-05 16:49:58 +08:00
fengmk2
3872b7dfdf replace dist.u.qiniudn.com with cnpmjs.org/dist 2014-03-05 13:20:45 +08:00
fengmk2
e340ce081a Release 0.3.5 2014-03-05 13:18:39 +08:00
dead_horse
170a689168 Merge pull request #250 from cnpm/dist
redirect /dist/xxx.tgz => http://dist.u.qiniudn.com/xxx.tgz fixed #249
2014-03-05 11:08:05 +08:00
fengmk2
7794d1099d redirect /dist/xxx.tgz => http://dist.u.qiniudn.com/xxx.tgz fixed #249 2014-03-05 11:00:41 +08:00
dead_horse
bd8686c448 Merge pull request #247 from cnpm/redirect
redirect /name to /package/name when /name is 404. fixed #245
2014-03-04 23:54:48 +08:00
fengmk2
d8a76d03e0 redirect /name to /package/name when /name is 404. fixed #245 2014-03-04 23:22:32 +08:00
dead_horse
4460810799 Merge pull request #244 from cnpm/missing-propertys
Add missing properies and sync missing star users. fixed #235
2014-03-04 21:35:57 +08:00
fengmk2
4124525eb8 Add missing properies and sync missing star users. fixed #235
* add readmeFilename and missing created, modified time.
* Sync star users on pkg sync flow
* Show star users on registry.show
* Show star users on web package show
* Need to create new table module_star
2014-03-04 21:20:43 +08:00
fengmk2
ff50946cd3 Release 0.3.4 2014-03-04 16:32:25 +08:00
fengmk2
3637eeefdd add cov 2014-03-04 16:31:22 +08:00
dead_horse
cecb41e6ea Merge pull request #243 from cnpm/coverage
use istanbul run test coverage
2014-03-03 21:52:52 +08:00
fengmk2
72b3240ef5 use istanbul run test coverage 2014-03-03 21:02:11 +08:00
dead_horse
869d5681ce Merge pull request #242 from cnpm/gzip
gzip support. fix #241
2014-03-03 16:45:37 +08:00
fengmk2
ff1bfd5acc gzip support. fix #241 2014-03-03 16:44:29 +08:00
dead_horse
7e3129c8b8 Merge pull request #240 from stanzheng/patch-1
readme spelling patch
2014-03-03 00:42:27 +08:00
Stanley Zheng
0618c732d7 thanks dead_horse 2014-03-02 11:39:30 -05:00
Stanley Zheng
9b34ab408c readme spelling patch 2014-03-02 11:10:40 -05:00
fengmk2
d990d3aa91 Merge pull request #234 from cnpm/issue233-remove-readme
Issue233 remove readme
2014-02-28 23:25:08 +08:00
dead_horse
03ef728049 default readme to null, fixed #233 2014-02-28 22:37:45 +08:00
dead_horse
f00ed85d41 remove readme in versions 2014-02-28 22:32:44 +08:00
fengmk2
685af2a367 Release 0.3.3 2014-02-28 17:41:09 +08:00
dead_horse
818f216fb4 Merge pull request #232 from cnpm/host-hotfix
get request host from request.headers
2014-02-28 16:36:44 +08:00
fengmk2
1b47495565 get request host from request.headers 2014-02-28 16:35:51 +08:00
dead_horse
0ab314f27c Merge pull request #231 from cnpm/bug-fix
fix deps display bug#230 and nsf.url TypeError#229
2014-02-28 15:40:04 +08:00
fengmk2
95076c8787 fix deps display bug#230 and nsf.url TypeError#229 2014-02-28 15:35:37 +08:00
fengmk2
4e6eb0a9cc Release 0.3.2 2014-02-28 14:46:17 +08:00
fengmk2
29f17dd5d1 Merge pull request #228 from cnpm/update-sess
update koa-sess and koa-redis
2014-02-28 14:42:03 +08:00
dead_horse
3a48637ef1 update koa-sess and koa-redis 2014-02-28 14:40:47 +08:00
fengmk2
e1029b005f Merge pull request #227 from cnpm/fix-nfs
Fix nfs
2014-02-28 12:20:43 +08:00
dead_horse
66771dfc3b fix sync all test 2014-02-28 12:15:30 +08:00
dead_horse
66f05a2f07 remove nfs.downloadStream first, fix tmppath error 2014-02-28 12:04:28 +08:00
fengmk2
6660cabbb6 Merge pull request #226 from cnpm/giturl-bug-fix
fix fengmk2/giturl#1 bug
2014-02-27 20:53:33 +08:00
fengmk2
fbe1971957 fix fengmk2/giturl#1 bug 2014-02-27 20:52:19 +08:00
45 changed files with 699 additions and 177 deletions

1
.gitignore vendored
View File

@@ -22,3 +22,4 @@ docs/web/history.md
view/web/_layout.html
bin/mysql.js
bin/test.sql
coverage/

4
.jshintignore Normal file
View File

@@ -0,0 +1,4 @@
node_modules/
coverage/
.tmp/
.git/

95
.jshintrc Normal file
View File

@@ -0,0 +1,95 @@
{
// JSHint Default Configuration File (as on JSHint website)
// See http://jshint.com/docs/ for more details
"maxerr" : 50, // {int} Maximum error before stopping
// Enforcing
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
"camelcase" : false, // true: Identifiers must be in camelCase
"curly" : true, // true: Require {} for every new block or scope
"eqeqeq" : true, // true: Require triple equals (===) for comparison
"forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty()
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
"indent" : false, // {int} Number of spaces to use for indentation
"latedef" : false, // true: Require variables/functions to be defined before being used
"newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
"noempty" : true, // true: Prohibit use of empty blocks
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
"plusplus" : false, // true: Prohibit use of `++` & `--`
"quotmark" : false, // Quotation mark consistency:
// false : do nothing (default)
// true : ensure whatever is used is consistent
// "single" : require single quotes
// "double" : require double quotes
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
"unused" : false, // true: Require all defined variables be used
"strict" : true, // true: Requires all functions run in ES5 Strict Mode
"trailing" : false, // true: Prohibit trailing whitespaces
"maxparams" : false, // {int} Max number of formal params allowed per function
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
"maxstatements" : false, // {int} Max number statements per function
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
"maxlen" : false, // {int} Max number of characters per line
// Relaxing
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
"boss" : true, // true: Tolerate assignments where comparisons would be expected
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
"eqnull" : false, // true: Tolerate use of `== null`
"es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
"esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`)
"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
// (ex: `for each`, multiple try/catch, function expression…)
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
"expr" : true, // true: Tolerate `ExpressionStatement` as Programs
"funcscope" : false, // true: Tolerate defining variables inside control statements"
"globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
"iterator" : false, // true: Tolerate using the `__iterator__` property
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
"laxbreak" : false, // true: Tolerate possibly unsafe line breakings
"laxcomma" : false, // true: Tolerate comma-first style coding
"loopfunc" : false, // true: Tolerate functions being defined in loops
"multistr" : true, // true: Tolerate multi-line strings
"proto" : false, // true: Tolerate using the `__proto__` property
"scripturl" : false, // true: Tolerate script-targeted URLs
"smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment
"shadow" : true, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
"validthis" : false, // true: Tolerate using this in a non-constructor function
// Environments
"browser" : true, // Web Browser (window, document, etc)
"couch" : false, // CouchDB
"devel" : true, // Development/debugging (alert, confirm, etc)
"dojo" : false, // Dojo Toolkit
"jquery" : false, // jQuery
"mootools" : false, // MooTools
"node" : true, // Node.js
"nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
"prototypejs" : false, // Prototype and Scriptaculous
"rhino" : false, // Rhino
"worker" : false, // Web Workers
"wsh" : false, // Windows Scripting Host
"yui" : false, // Yahoo User Interface
"noyield" : true, // allow generators without a yield
// Legacy
"nomen" : false, // true: Prohibit dangling `_` in variables
"onevar" : false, // true: Allow only one `var` statement per function
"passfail" : false, // true: Stop on first error
"white" : false, // true: Check against strict whitespace and indentation rules
// Custom Globals
"globals" : { // additional predefined global variables
// mocha
"describe": true,
"it": true,
"before": true,
"afterEach": true,
"beforeEach": true,
"after": true
}
}

View File

@@ -10,3 +10,6 @@ backup/*.json
backup/*.gz
view/web/_layout.html
bin/mysql.js
coverage/
.jshintrc
.jshintignore

View File

@@ -1,4 +1,3 @@
language: node_js
node_js:
- '0.11'
script: make test-coveralls

View File

@@ -1,5 +1,5 @@
# Ordered by date of first contribution.
# Auto-generated by 'contributors' on Fri, 24 Jan 2014 08:35:59 GMT.
# Auto-generated by 'contributors' on Mon, 03 Mar 2014 13:01:28 GMT.
# https://github.com/xingrz/node-contributors
fengmk2 <fengmk2@gmail.com> (https://github.com/fengmk2)

View File

@@ -1,4 +1,47 @@
0.3.6 / 2014-03-06
==================
* install missing package should sync it from source npm. fixed #252
* npm publish dont contains .jshint*
* npm test run jshint
* Add jshint check: $ make jshint
* use `yield* next` instead of `yield next`
* replace dist.u.qiniudn.com with cnpmjs.org/dist
0.3.5 / 2014-03-05
==================
* redirect /dist/xxx.tgz => http://dist.u.qiniudn.com/xxx.tgz fixed #249
* redirect /name to /package/name when /name is 404. fixed #245
* Add missing properies and sync missing star users. fixed #235
0.3.4 / 2014-03-04
==================
* add cov
* use istanbul run test coverage
* gzip support. fix #241
* readme spelling patch (@stanzheng)
* default readme to null, fixed #233
* remove readme in versions
0.3.3 / 2014-02-28
==================
* Merge pull request #232 from cnpm/host-hotfix
* get request host from request.headers
* Merge pull request #231 from cnpm/bug-fix
* fix deps display bug#230 and nsf.url TypeError#229
0.3.2 / 2014-02-28
==================
* update koa-sess and koa-redis
* fix sync all test
* remove nfs.downloadStream first, fix tmppath error
* fix fengmk2/giturl#1 bug
0.3.1 / 2014-02-27
==================

View File

@@ -4,36 +4,37 @@ TIMEOUT = 30000
MOCHA_OPTS =
install:
@npm install --registry=http://registry.cnpmjs.org --cache=${HOME}/.npm/.cache/cnpm --disturl=http://dist.u.qiniudn.com
@npm install --registry=http://registry.cnpmjs.org \
--disturl=http://cnpmjs.org/dist
test: install
@NODE_ENV=test ./node_modules/mocha/bin/mocha \
--harmony-generators \
jshint:
@./node_modules/.bin/jshint ./
test:
@NODE_ENV=test node --harmony \
node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha \
-- -u exports \
--reporter $(REPORTER) \
--timeout $(TIMEOUT) \
--require should \
$(MOCHA_OPTS) \
$(TESTS)
@-$(MAKE) check-coverage
test-cov:
@$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=travis-cov
check-coverage:
@./node_modules/.bin/istanbul check-coverage \
--statements 100 \
--functions 100 \
--branches 100 \
--lines 100
test-cov-html:
@rm -f coverage.html
@$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=html-cov > coverage.html
@ls -lh coverage.html
cov:
@./node_modules/.bin/cov coverage
test-coveralls: test
@echo TRAVIS_JOB_ID $(TRAVIS_JOB_ID)
@-$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=mocha-lcov-reporter | ./node_modules/coveralls/bin/coveralls.js
contributors:
@./node_modules/.bin/contributors -f plain -o AUTHORS
test-all: test test-cov
contributors: install
@./node_modules/contributors/bin/contributors -f plain -o AUTHORS
autod: install
@./node_modules/.bin/autod -w -e public,view,docs,backup
autod:
@./node_modules/.bin/autod -w -e public,view,docs,backup,coverage
@$(MAKE) install
.PHONY: test

View File

@@ -1,7 +1,7 @@
cnpmjs.org
=======
[![Build Status](https://secure.travis-ci.org/cnpm/cnpmjs.org.png)](http://travis-ci.org/cnpm/cnpmjs.org) [![Coverage Status](https://coveralls.io/repos/cnpm/cnpmjs.org/badge.png)](https://coveralls.io/r/cnpm/cnpmjs.org) [![Dependency Status](https://gemnasium.com/cnpm/cnpmjs.org.png)](https://gemnasium.com/cnpm/cnpmjs.org)
[![Build Status](https://secure.travis-ci.org/cnpm/cnpmjs.org.png)](http://travis-ci.org/cnpm/cnpmjs.org) [![Dependency Status](https://gemnasium.com/cnpm/cnpmjs.org.png)](https://gemnasium.com/cnpm/cnpmjs.org)
[![NPM](https://nodei.co/npm/cnpmjs.org.png?downloads=true&stars=true)](https://nodei.co/npm/cnpmjs.org/)
@@ -11,7 +11,8 @@ cnpmjs.org
Private npm registry and web for Enterprise, base on [koa](http://koajs.com/), MySQL and [Simple Store Service](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide).
@[JacksonTian](https://github.com/JacksonTian/) had a talk about [private npm](https://speakerdeck.com/jacksontian/qi-ye-ji-node-dot-jskai-fa).
* @[dead-horse](https://github.com/dead-horse): [What is cnpm?](http://deadhorse.me/slides/cnpmjs.html)
* @[JacksonTian](https://github.com/JacksonTian/) had a talk about [private npm](https://speakerdeck.com/jacksontian/qi-ye-ji-node-dot-jskai-fa).
![cnpm](https://docs.google.com/drawings/d/12QeQfGalqjsB77mRnf5Iq5oSXHCIUTvZTwECMonqCmw/pub?w=480&h=360)
@@ -19,7 +20,7 @@ Private npm registry and web for Enterprise, base on [koa](http://koajs.com/), M
## Install
```bash
$ npm install --registry=http://r.cnpmjs.org --disturl=http://dist.u.qiniudn.com
$ npm install --registry=http://r.cnpmjs.org --disturl=http://cnpmjs.org/dist
```
## Usage

View File

@@ -15,6 +15,7 @@
* Module dependencies.
*/
var thunkify = require('thunkify-wrap');
var ready = require('ready');
var mysql = require('mysql');
var config = require('../config');
@@ -35,6 +36,10 @@ var pool = mysql.createPool({
exports.pool = pool;
exports.query = function (sql, values, cb) {
if (typeof values === 'function') {
cb = values;
values = null;
}
pool.query(sql, values, function (err, rows) {
cb(err, rows);
});
@@ -59,6 +64,8 @@ exports.escape = function (val) {
ready(exports);
thunkify(exports);
function init() {
exports.query('show tables', function (err, rows) {
if (err) {

View File

@@ -72,6 +72,7 @@ var config = {
debug: false
},
disturl: 'http://dist.u.qiniudn.com',
logoURL: 'http://ww4.sinaimg.cn/large/69c1d4acgw1ebfly5kjlij208202oglr.jpg',
registryHost: 'r.cnpmjs.org',
customFooter: '', // you can add copyright and site total script html here

View File

@@ -31,11 +31,11 @@ var Module = require('../../proxy/module');
var Total = require('../../proxy/total');
var nfs = require('../../common/nfs');
var common = require('../../lib/common');
var Log = require('../../proxy/module_log');
var DownloadTotal = require('../../proxy/download');
var SyncModuleWorker = require('../../proxy/sync_module_worker');
var logger = require('../../common/logger');
var ModuleDeps = require('../../proxy/module_deps');
var ModuleStar = require('../../proxy/module_star');
/**
* show all version of a module
@@ -43,9 +43,19 @@ var ModuleDeps = require('../../proxy/module_deps');
exports.show = function *(next) {
var name = this.params.name;
var r = yield [Module.listTags(name), Module.listByName(name)];
var r = yield [
Module.listTags(name),
Module.listByName(name),
ModuleStar.listUsers(name)
];
var tags = r[0];
var rows = r[1];
var users = r[2];
var userMap = {};
for (var i = 0; i < users.length; i++) {
userMap[users[i]] = true;
}
users = userMap;
debug('show module, user: %s, allowSync: %s, isAdmin: %s',
this.session.name, this.session.allowSync, this.session.isAdmin);
@@ -69,7 +79,7 @@ exports.show = function *(next) {
var nextMod = null;
var latestMod = null;
var readme = null;
// set tags
var distTags = {};
for (var i = 0; i < tags.length; i++) {
@@ -81,6 +91,8 @@ exports.show = function *(next) {
var versions = {};
var times = {};
var attachments = {};
var createdTime = null;
var modifiedTime = null;
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
if (row.version === 'next') {
@@ -91,10 +103,30 @@ exports.show = function *(next) {
common.setDownloadURL(pkg, this);
pkg._cnpm_publish_time = row.publish_time;
versions[pkg.version] = pkg;
times[pkg.version] = row.publish_time ? new Date(row.publish_time) : row.gmt_modified;
var t = times[pkg.version] = row.publish_time ? new Date(row.publish_time) : row.gmt_modified;
if ((!distTags.latest && !latestMod) || distTags.latest === row.version) {
latestMod = row;
readme = pkg.readme;
}
delete pkg.readme;
if (!createdTime || t < createdTime) {
createdTime = t;
}
if (!modifiedTime || t > modifiedTime) {
modifiedTime = t;
}
}
if (modifiedTime && createdTime) {
var ts = {
modified: modifiedTime,
created: createdTime,
};
for (var t in times) {
ts[t] = times[t];
}
times = ts;
}
if (!latestMod) {
@@ -110,21 +142,29 @@ exports.show = function *(next) {
rev = String(nextMod.id);
}
var pkg = latestMod.package;
var info = {
_id: name,
_rev: rev,
name: name,
description: latestMod.package.description,
description: pkg.description,
"dist-tags": distTags,
maintainers: latestMod.package.maintainers,
maintainers: pkg.maintainers,
time: times,
author: latestMod.package.author,
repository: latestMod.package.repository,
users: users,
author: pkg.author,
repository: pkg.repository,
versions: versions,
readme: latestMod.package.readme,
readme: readme,
_attachments: attachments,
};
info.readmeFilename = pkg.readmeFilename;
info.homepage = pkg.homepage;
info.bugs = pkg.bugs;
info.license = pkg.license;
debug('show module %s: %s, latest: %s', name, rev, latestMod.version);
this.body = info;
@@ -182,17 +222,22 @@ exports.download = function *(next) {
var version = filename.slice(name.length + 1, -4);
var row = yield Module.get(name, version);
// can not get dist
var url = nfs.url(common.getCDNKey(name, filename));
var url = null;
if (typeof nfs.url === 'function') {
url = nfs.url(common.getCDNKey(name, filename));
}
if (!row || !row.package || !row.package.dist) {
if (!nfs.url) {
return yield next;
if (!url) {
return yield* next;
}
this.status = 302;
this.set('Location', url);
_downloads[name] = (_downloads[name] || 0) + 1;
return;
}
var dist = row.package.dist;
if (!dist.key) {
debug('get tarball by 302');
@@ -203,8 +248,8 @@ exports.download = function *(next) {
}
// else use `dist.key` to get tarball from nfs
if (!nfs.downloadStream && !nfs.download) {
return yield next;
if (!nfs.download) {
return yield* next;
}
_downloads[name] = (_downloads[name] || 0) + 1;
@@ -216,13 +261,9 @@ exports.download = function *(next) {
this.set('Content-Disposition', 'attachment; filename="' + filename + '"');
this.set('ETag', dist.shasum);
if (nfs.downloadStream) {
yield nfs.downloadStream(dist.key, this.res, {timeout: DOWNLOAD_TIMEOUT});
return;
}
// use download file api
var tmpPath = path.join(config.uploadDir, utility.randomString() + dist.key);
var tmpPath = path.join(config.uploadDir,
utility.randomString() + dist.key.replace(/\//g, '-'));
function cleanup() {
fs.unlink(tmpPath, utility.noop);
}
@@ -233,8 +274,8 @@ exports.download = function *(next) {
this.throw(err);
}
var tarball = fs.createReadStream(tmpPath);
tarball.on('error', cleanup);
tarball.on('end', cleanup);
tarball.once('error', cleanup);
tarball.once('end', cleanup);
this.body = tarball;
};
@@ -286,7 +327,7 @@ exports.upload = function *(next) {
var length = Number(this.get('content-length')) || 0;
if (!length || !this.is('application/octet-stream')) {
debug('request length or type error');
return yield next;
return yield* next;
}
var username = this.session.name;
@@ -301,7 +342,7 @@ exports.upload = function *(next) {
var mod = yield Module.getById(id);
if (!mod) {
debug('can not get this module');
return yield next;
return yield* next;
}
if (!common.isMaintainer(this, mod.package.maintainers) || mod.name !== name) {
this.status = 403;
@@ -327,7 +368,7 @@ exports.upload = function *(next) {
var dataSize = 0;
var buf;
while(buf = yield coRead(this.req)) {
while (buf = yield coRead(this.req)) {
shasum.update(buf);
dataSize += buf.length;
yield coWrite(ws, buf);
@@ -409,7 +450,7 @@ exports.updateLatest = function *(next) {
var nextMod = yield Module.get(name, 'next');
if (!nextMod) {
debug('can not get nextMod');
return yield next;
return yield* next;
}
var match = nextMod.package.maintainers.filter(function (item) {
return item.name === username;
@@ -657,7 +698,7 @@ exports.removeWithVersions = function *(next) {
// step1: list all the versions
var mods = yield Module.listByName(name);
if (!mods || !mods.length) {
return yield next;
return yield* next;
}
// step2: check permission
@@ -740,7 +781,7 @@ exports.removeTar = function *(next) {
var mod = yield Module.getById(id);
if (!mod) {
return yield next;
return yield* next;
}
if (!common.isMaintainer(this, mod.package.maintainers) || mod.name !== name) {
@@ -759,20 +800,20 @@ exports.removeTar = function *(next) {
exports.removeAll = function *(next) {
debug('remove all the module with name: %s, id: %s', this.params.name, this.params.rev);
var id = Number(this.params.rev);
// var id = Number(this.params.rev);
var name = this.params.name;
var username = this.session.name;
// var username = this.session.name;
var mods = yield Module.listByName(name);
debug('removeAll module %s: %d', name, mods.length);
var mod = mods[0];
if (!mod) {
return yield next;
return yield* next;
}
if (!common.isMaintainer(this, mod.package.maintainers) || mod.name !== name) {
res.status = 403;
res.body = {
this.status = 403;
this.body = {
error: 'no_perms',
reason: 'Current user can not delete this tarball'
};
@@ -818,13 +859,13 @@ function parseModsForList(updated, mods, ctx) {
return results;
}
exports.listAllModules = function *(next) {
exports.listAllModules = function *() {
var updated = Date.now();
var mods = yield Module.listSince(0);
this.body = parseModsForList(updated, mods, this);
};
exports.listAllModulesSince = function *(next) {
exports.listAllModulesSince = function *() {
var query = this.query || {};
if (query.stale !== 'update_after') {
this.status = 400;
@@ -842,7 +883,7 @@ exports.listAllModulesSince = function *(next) {
this.body = parseModsForList(updated, mods, this);
};
exports.listAllModuleNames = function *(next) {
exports.listAllModuleNames = function *() {
this.body = (yield Module.listShort()).map(function (m) {
return m.name;
});

View File

@@ -16,14 +16,13 @@
*/
var debug = require('debug')('cnpmjs.org:controllers:registry');
var logger = require('../../common/logger');
var User = require('../../proxy/user');
exports.show = function *(next) {
var name = this.params.name;
var user = yield User.get(name);
if (!user) {
return yield next;
return yield* next;
}
this.etag = '"' + user.rev + '"';
var data = {
@@ -111,7 +110,7 @@ exports.update = function *(next) {
var name = this.params.name;
var rev = this.params.rev;
if (!name || !rev) {
return yield next;
return yield* next;
}
debug('update: %s, rev: %s, session.name: %s', name, rev, this.session.name);

View File

@@ -61,11 +61,10 @@ exports.sync = function *() {
exports.getSyncLog = function *(next) {
var logId = this.params.id;
var name = this.params.name;
var offset = Number(this.query.offset) || 0;
var row = yield Log.get(logId);
if (!row) {
return yield next;
return yield* next;
}
var log = row.log.trim();

23
controllers/web/dist.js Normal file
View File

@@ -0,0 +1,23 @@
/**!
* cnpmjs.org - controllers/web/dist.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
"use strict";
/**
* Module dependencies.
*/
var config = require('../../config');
exports.redirect = function *(next) {
var params = this.params;
var url = config.disturl + (params[0] || '/');
this.redirect(url);
};

View File

@@ -28,6 +28,7 @@ var sync = require('../sync');
var Log = require('../../proxy/module_log');
var ModuleDeps = require('../../proxy/module_deps');
var setDownloadURL = require('../../lib/common').setDownloadURL;
var ModuleStar = require('../../proxy/module_star');
exports.display = function *(next) {
var params = this.params;
@@ -47,18 +48,23 @@ exports.display = function *(next) {
var r = yield [
Module[getPackageMethod].apply(Module, getPackageArgs),
down.total(name),
ModuleDeps.list(name)
ModuleDeps.list(name),
ModuleStar.listUsers(name),
];
var pkg = r[0];
var download = r[1];
var dependents = r[2];
var dependents = (r[2] || []).map(function (item) {
return item.deps;
});
var users = r[3];
if (!pkg || !pkg.package) {
return yield next;
return yield* next;
}
pkg.package.fromNow = moment(pkg.publish_time).fromNow();
pkg = pkg.package;
pkg.users = users;
pkg.readme = marked(pkg.readme || '');
if (!pkg.readme) {
pkg.readme = pkg.description || '';
@@ -90,7 +96,7 @@ exports.display = function *(next) {
}
if (pkg.repository && pkg.repository.url) {
pkg.repository.weburl = giturl.parse(pkg.repository.url);
pkg.repository.weburl = giturl.parse(pkg.repository.url) || pkg.repository.url;
}
setLicense(pkg);

View File

@@ -23,7 +23,7 @@ exports.display = function *(next) {
var packages = r[0];
var user = r[1];
if (!user && !packages.length) {
return yield next;
return yield* next;
}
user = {
name: name,

View File

@@ -1,4 +1,4 @@
/*!
/**!
* cnpmjs.org - dispatch.js
*
* Copyright(c) cnpmjs.org and other contributors.
@@ -17,7 +17,6 @@
var path = require('path');
var util = require('util');
var fs = require('fs');
var cluster = require('cluster');
var config = require('./config');
var workerPath = path.join(__dirname, 'worker.js');

View File

@@ -25,6 +25,16 @@ CREATE TABLE `module_keyword` (
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module keyword';
CREATE TABLE `module_star` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name',
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
PRIMARY KEY (`id`),
UNIQUE KEY `user_module_name` (`user`,`name`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module star';
CREATE TABLE `module` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',

View File

@@ -103,13 +103,13 @@ npm install -g cnpm
alias lnpm='cnpm --registry=http://localhost:7001\
--registryweb=http://localhost:7002\
--cache=$HOME/.npm/.cache/lnpm\
--disturl=http://dist.u.qiniudn.com\
--disturl=http://cnpmjs.org/dist\
--userconfig=$HOME/.lnpmrc'
#or put this in .zshrc or .bashrc
echo "#lnpm alias\nalias lnpm='cnpm --registry=http://localhost:7001\
--registryweb=http://localhost:7002\
--cache=$HOME/.npm/.cache/lnpm\
--disturl=http://dist.u.qiniudn.com\
--disturl=http://cnpmjs.org/dist\
--userconfig=$HOME/.lnpmrc'" >> $HOME/.zshrc && source $HOME/.zshrc
```

View File

@@ -1,6 +1,6 @@
# cnpmjs.org: Private npm registry and web for Enterprise
[![Build Status](https://secure.travis-ci.org/cnpm/cnpmjs.org.png)](http://travis-ci.org/cnpm/cnpmjs.org) [![Coverage Status](https://coveralls.io/repos/cnpm/cnpmjs.org/badge.png)](https://coveralls.io/r/cnpm/cnpmjs.org) [![Dependency Status](https://gemnasium.com/cnpm/cnpmjs.org.png)](https://gemnasium.com/cnpm/cnpmjs.org)
[![Build Status](https://secure.travis-ci.org/cnpm/cnpmjs.org.png)](http://travis-ci.org/cnpm/cnpmjs.org) [![Dependency Status](https://gemnasium.com/cnpm/cnpmjs.org.png)](https://gemnasium.com/cnpm/cnpmjs.org)
[![NPM](https://nodei.co/npm/cnpmjs.org.png?downloads=true&stars=true)](https://nodei.co/npm/cnpmjs.org/)
@@ -8,7 +8,8 @@
> Private npm registry and web for Enterprise, base on [koa](http://koajs.com/), MySQL and [Simple Store Service](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide).
@[JacksonTian](https://github.com/JacksonTian/) had a talk about [private npm](https://speakerdeck.com/jacksontian/qi-ye-ji-node-dot-jskai-fa).
* @[dead-horse](https://github.com/dead-horse): [What is cnpm?](http://deadhorse.me/slides/cnpmjs.html)
* @[JacksonTian](https://github.com/JacksonTian/) had a talk about [private npm](https://speakerdeck.com/jacksontian/qi-ye-ji-node-dot-jskai-fa).
## Install your private npm registry
@@ -121,13 +122,13 @@ alias it:
```bash
alias cnpm="npm --registry=http://r.cnpmjs.org \
--cache=$HOME/.npm/.cache/cnpm \
--disturl=http://dist.u.qiniudn.com \
--disturl=http://cnpmjs.org/dist \
--userconfig=$HOME/.cnpmrc"
#Or alias it in .bashrc or .zshrc
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=http://r.cnpmjs.org \
--cache=$HOME/.npm/.cache/cnpm \
--disturl=http://dist.u.qiniudn.com \
--disturl=http://cnpmjs.org/dist \
--userconfig=$HOME/.cnpmrc"' >> ~/.zshrc && source ~/.zshrc
```
@@ -139,7 +140,7 @@ $ npm install cnpm -g
### install
Install package from [r.cnpmjs.org](http://r.cnpmjs.org). When isntall a package or version not exist, it will try to install from official registry([registry.npmjs.org](http://registry.npmjs.org)), and sync this package to cnpm in the backend.
Install package from [r.cnpmjs.org](http://r.cnpmjs.org). When installing a package or version does not exist, it will try to install from the official registry([registry.npmjs.org](http://registry.npmjs.org)), and sync this package to cnpm in the backend.
```
$ cnpm install [name]

View File

@@ -1,4 +1,4 @@
/*!
/**!
* cnpmjs.org - index.js
*
* Copyright(c) cnpmjs.org and other contributors.

View File

@@ -31,7 +31,7 @@ exports.getCDNKey = function (name, filename) {
exports.setDownloadURL = function (pkg, ctx, host) {
if (pkg.dist) {
host = host || ctx.host;
host = host || ctx.get('host') || ctx.host;
pkg.dist.tarball = util.format('%s://%s/%s/download/%s-%s.tgz',
ctx.protocol,
host, pkg.name, pkg.name, pkg.version);

View File

@@ -25,24 +25,24 @@ module.exports = function (options) {
if (!this.session) {
// redis crash
this.session = {};
return yield next;
return yield *next;
}
this.session.onlySync = config.enablePrivate ? true : false;
if (this.session.name) {
this.session.isAdmin = common.isAdmin(this.session.name);
debug('auth exists user: %s, onlySync: %s, isAdmin: %s, headers: %j',
this.session.name, this.session.onlySync, this.session.isAdmin, this.header);
return yield next;
return yield *next;
}
var authorization = (this.get('authorization') || '').split(' ')[1] || '';
authorization = authorization.trim();
if (!authorization) {
return yield next;
return yield *next;
}
authorization = new Buffer(authorization, 'base64').toString().split(':');
if (authorization.length !== 2) {
return yield next;
return yield *next;
}
var username = authorization[0];
@@ -53,13 +53,13 @@ module.exports = function (options) {
debug('auth fail user: %j, headers: %j', row, this.header);
this.session.name = null;
this.session.isAdmin = false;
return yield next;
return yield *next;
}
this.session.name = row.name;
this.session.isAdmin = common.isAdmin(this.session.name);
debug('auth pass user: %j, onlySync: %s, isAdmin: %s, headers: %j',
row, this.session.onlySync, this.session.isAdmin, this.header);
yield next;
yield *next;
};
};

View File

@@ -23,5 +23,5 @@ module.exports = function *login(next) {
};
return;
}
yield next;
yield *next;
};

View File

@@ -30,5 +30,5 @@ module.exports = function *publishable(next) {
this.charset = 'utf-8';
this.body = template.replace('${host}', this.host);
}
yield next;
yield *next;
};

View File

@@ -24,5 +24,5 @@ module.exports = function *publishable(next) {
};
return;
}
yield next;
yield *next;
};

View File

@@ -15,7 +15,7 @@
*/
module.exports = function *notFound(next) {
yield next;
yield *next;
if (this.status) {
return;

View File

@@ -24,11 +24,11 @@ var config = require('../config');
module.exports = function *(next) {
if (!config.syncByInstall || !config.enablePrivate) {
// only config.enablePrivate should enable sync on install
return yield next;
return yield *next;
}
// request not by node, consider it request from web
if (this.get('user-agent') && this.get('user-agent').indexOf('node') !== 0) {
return yield next;
return yield *next;
}
this.session.allowSync = true;
@@ -37,9 +37,6 @@ module.exports = function *(next) {
this.session.allowSync = false;
}
// TODO: allow sync will let publish sync package...
this.session.allowSync = false;
debug('%s allowSync: %s', this.session.name, this.session.allowSync);
yield next;
yield *next;
};

View File

@@ -15,11 +15,17 @@
*/
module.exports = function *notFound(next) {
yield next;
yield *next;
if (this.status) {
return;
}
var m = /^\/([\w\-\_\.]+)\/?$/.exec(this.url);
if (m) {
return this.redirect('/package/' + m[1]);
}
this.status = 404;
this.type = 'text/html';
this.charset = 'utf-8';

View File

@@ -1,35 +1,32 @@
{
"name": "cnpmjs.org",
"version": "0.3.1",
"version": "0.3.6",
"description": "Private npm registry and web for Enterprise, base on MySQL and Simple Store Service",
"main": "index.js",
"scripts": {
"test": "make test-all",
"test": "make install && make jshint && make test",
"start": "./bin/nodejsctl start && cp History.md docs/web/history.md",
"status": "./bin/nodejsctl status",
"stop": "./bin/nodejsctl stop"
},
"config": {
"blanket": {
"pattern": "//^((?!(node_modules|test|common)).)*$/"
},
"travis-cov": {
"threshold": 90
"cov": {
"threshold": 83
}
},
"dependencies": {
"co": "3.0.4",
"co-read": "0.0.1",
"co-write": "0.3.0",
"debug": "0.7.4",
"eventproxy": "0.2.6",
"forward": "0.0.4",
"giturl": "0.0.1",
"eventproxy": "0.3.0",
"giturl": "0.0.2",
"graceful": "0.0.6",
"gravatar": "1.0.6",
"humanize-number": "0.0.2",
"koa": "0.5.0",
"koa-markdown": "0.0.2",
"koa-middlewares": "0.0.5",
"koa-middlewares": "0.0.8",
"logfilestream": "0.1.0",
"marked": "0.3.1",
"microtime": "0.5.1",
@@ -42,22 +39,21 @@
"qn": "0.2.0",
"ready": "0.1.1",
"semver": "2.2.1",
"thunkify-wrap": "0.0.1",
"urllib": "0.5.5",
"thunkify-wrap": "0.0.5",
"urllib": "0.5.6",
"utility": "0.1.10"
},
"devDependencies": {
"autod": ">=0.0.13",
"blanket": "*",
"contributors": "*",
"coveralls": "*",
"cov": "*",
"istanbul": "git://github.com/gotwarlost/istanbul.git#harmony",
"jshint": "*",
"mm": "0.2.0",
"mocha": "*",
"mocha-lcov-reporter": "*",
"pedding": "0.0.3",
"should": "3.1.3",
"supertest": "0.9.0",
"travis-cov": "*"
"supertest": "0.9.0"
},
"homepage": "https://github.com/cnpm/cnpmjs.org",
"repository": {

47
proxy/module_star.js Normal file
View File

@@ -0,0 +1,47 @@
/**!
* cnpmjs.org - proxy/module_star.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var mysql = require('../common/mysql');
exports.add = function *add(name, user) {
var sql = 'INSERT INTO module_star(name, user) VALUES(?, ?);';
try {
yield mysql.query(sql, [name, user]);
} catch (err) {
if (err.code !== 'ER_DUP_ENTRY') {
throw err;
}
}
};
exports.remove = function *(name, user) {
var sql = 'DELETE FROM module_star WHERE name = ? AND user = ?;';
return yield mysql.query(sql, [name, user]);
};
exports.listUsers = function *(name) {
var sql = 'SELECT user FROM module_star WHERE name = ?;';
return (yield mysql.query(sql, [name])).map(function (r) {
return r.user;
});
};
exports.listUserModules = function *(user) {
var sql = 'SELECT name FROM module_star WHERE user = ?;';
return (yield mysql.query(sql, [user])).map(function (r) {
return r.name;
});
};

View File

@@ -15,6 +15,7 @@
* Module dependencies.
*/
var co = require('co');
var thunkify = require('thunkify-wrap');
var debug = require('debug')('cnpmjs.org:proxy:sync_module_worker');
var EventEmitter = require('events').EventEmitter;
@@ -33,6 +34,7 @@ var Module = require('./module');
var ModuleDeps = require('./module_deps');
var Log = require('./module_log');
var config = require('../config');
var ModuleStar = require('./module_star');
function SyncModuleWorker(options) {
EventEmitter.call(this);
@@ -145,6 +147,36 @@ SyncModuleWorker.prototype.next = function (concurrencyId) {
});
};
function _listStarUsers(modName, callback) {
co(function *() {
var users;
var err;
try {
users = yield ModuleStar.listUsers(modName);
var userMap = {};
for (var i = 0; i < users.length; i++) {
userMap[users[i]] = true;
}
users = userMap;
} catch (e) {
err = e;
}
callback(err, users);
})();
}
function _addStar(modName, username, callback) {
co(function *() {
var err;
try {
yield ModuleStar.add(modName, username);
} catch (e) {
err = e;
}
callback(err);
})();
}
SyncModuleWorker.prototype._sync = function (name, pkg, callback) {
var username = this.username;
var that = this;
@@ -194,12 +226,24 @@ SyncModuleWorker.prototype._sync = function (name, pkg, callback) {
ep.emit('existsTags', tags);
}));
_listStarUsers(name, ep.done('existsStarUsers'));
var missingVersions = [];
var missingTags = [];
var missingDescriptions = [];
var missingReadmes = [];
var missingStarUsers = [];
ep.all('existsMap', 'existsTags', 'existsStarUsers', function (map, tags, existsStarUsers) {
var starUsers = pkg.users || {};
for (var k in starUsers) {
if (!existsStarUsers[k]) {
missingStarUsers.push(k);
}
}
that.log(' [%s] found %d missing star users', name, missingStarUsers.length);
ep.all('existsMap', 'existsTags', function (map, tags) {
var times = pkg.time || {};
pkg.versions = pkg.versions || {};
var versionNames = Object.keys(times);
@@ -300,7 +344,7 @@ SyncModuleWorker.prototype._sync = function (name, pkg, callback) {
return a.publish_time - b.publish_time;
});
missingVersions = versions;
that.log(' [%s] %d versions', name, versions.length);
that.log(' [%s] %d versions need to sync', name, versions.length);
ep.emit('syncModule', missingVersions.shift());
});
@@ -318,13 +362,16 @@ SyncModuleWorker.prototype._sync = function (name, pkg, callback) {
var nextVersion = missingVersions.shift();
if (!nextVersion) {
ep.unbind('syncModule');
return ep.emit('syncDone', result);
}
// next
ep.emit('syncModule', nextVersion);
});
});
ep.on('syncDone', function () {
ep.once('syncDone', function () {
if (missingDescriptions.length === 0) {
return ep.emit('descriptionDone');
}
@@ -346,7 +393,7 @@ SyncModuleWorker.prototype._sync = function (name, pkg, callback) {
});
});
ep.on('syncDone', function () {
ep.once('syncDone', function () {
if (missingTags.length === 0) {
return ep.emit('tagDone');
}
@@ -387,7 +434,28 @@ SyncModuleWorker.prototype._sync = function (name, pkg, callback) {
});
});
ep.all('tagDone', 'descriptionDone', 'readmeDone', function () {
ep.once('syncDone', function () {
if (missingStarUsers.length === 0) {
return ep.emit('starUserDone');
}
that.log(' [%s] saving %d star users', name, missingStarUsers.length);
missingStarUsers.forEach(function (username) {
_addStar(name, username, function (err) {
if (err) {
that.log(' add star user error, %s', err);
}
ep.emitLater('addStarUser');
});
});
ep.after('addStarUser', missingStarUsers.length, function () {
ep.emit('starUserDone');
});
});
ep.all('tagDone', 'descriptionDone', 'readmeDone', 'starUserDone',
function () {
// TODO: set latest version
callback(null, versionNames);
});

View File

@@ -1,4 +1,4 @@
/*!
/**!
* cnpmjs.org - routes/web.js
*
* Copyright(c) cnpmjs.org and other contributors.
@@ -19,6 +19,7 @@ var pkg = require('../controllers/web/package');
var user = require('../controllers/web/user');
var sync = require('../controllers/sync');
var total = require('../controllers/total');
var dist = require('../controllers/web/dist');
function routes(app) {
app.get('/total', total.show);
@@ -34,6 +35,8 @@ function routes(app) {
app.get('/sync', pkg.displaySync);
app.get('/_list/search/search', pkg.rangeSearch);
app.get(/^\/dist(\/.+)?/, dist.redirect);
}
module.exports = routes;

View File

@@ -18,8 +18,7 @@
var koa = require('koa');
var app = module.exports = koa();
var http = require('http');
var forward = require('forward');
var path = require('path');
var microtime = require('microtime');
var middlewares = require('koa-middlewares');
var routes = require('../routes/registry');
var logger = require('../common/logger');
@@ -27,9 +26,8 @@ var config = require('../config');
var session = require('../common/session');
var auth = require('../middleware/auth');
var notFound = require('../middleware/registry_not_found');
var rootdir = path.dirname(__dirname);
app.use(middlewares.rt({headerName: 'X-ReadTime'}));
app.use(middlewares.rt({headerName: 'X-ReadTime', timer: microtime}));
app.use(middlewares.rewrite('/favicon.ico', '/public/favicon.ico'));
@@ -40,6 +38,7 @@ app.use(middlewares.bodyParser({jsonLimit: config.jsonLimit}));
app.use(auth());
app.use(notFound);
app.use(middlewares.gzip());
app.use(middlewares.fresh());
app.use(middlewares.etag());

View File

@@ -18,6 +18,7 @@
var path = require('path');
var http = require('http');
var fs = require('fs');
var microtime = require('microtime');
var koa = require('koa');
var middlewares = require('koa-middlewares');
var markdown = require('koa-markdown');
@@ -32,7 +33,7 @@ var app = koa();
var rootdir = path.dirname(__dirname);
app.use(middlewares.rt({headerName: 'X-ReadTime'}));
app.use(middlewares.rt({headerName: 'X-ReadTime', timer: microtime}));
app.use(middlewares.staticCache(path.join(__dirname, '..', 'public'), {
buffer: !config.debug,
maxAge: config.debug ? 0 : 60 * 60 * 24 * 7,
@@ -45,6 +46,10 @@ app.use(session);
app.use(middlewares.bodyParser());
app.use(notFound);
app.use(middlewares.gzip());
app.use(middlewares.fresh());
app.use(middlewares.etag());
var viewDir = path.join(rootdir, 'view', 'web');
var docDir = path.join(rootdir, 'docs', 'web');

View File

@@ -79,7 +79,34 @@ describe('controllers/registry/module.test.js', function () {
etag = res.headers.etag;
res.body.should.have.keys('_id', '_rev', 'name', 'description',
'versions', 'dist-tags', 'readme', 'maintainers',
'time', 'author', 'repository', '_attachments');
'time', 'author', 'repository', '_attachments',
'users', 'readmeFilename', 'homepage', 'bugs', 'license');
res.body.name.should.equal('cnpmjs.org');
res.body.versions[Object.keys(res.body.versions)[0]]
.dist.tarball.should.include('/cnpmjs.org/download');
res.body.time.should.have.property('modified');
res.body.time.modified.should.be.a.String;
res.body.time.should.have.property('created');
res.body.time.created.should.be.a.String;
done();
});
});
it('should return module info and gzip when accept-encoding=gzip', function (done) {
request(app)
.get('/cnpmjs.org')
.set('accept-encoding', 'gzip')
.expect('content-encoding', 'gzip')
.expect(200, function (err, res) {
// console.log(res.headers)
should.not.exist(err);
// should have etag
res.headers.should.have.property('etag');
etag = res.headers.etag;
res.body.should.have.keys('_id', '_rev', 'name', 'description',
'versions', 'dist-tags', 'readme', 'maintainers',
'time', 'author', 'repository', '_attachments',
'users', 'readmeFilename', 'homepage', 'bugs', 'license');
res.body.name.should.equal('cnpmjs.org');
res.body.versions[Object.keys(res.body.versions)[0]].dist.tarball.should.include('/cnpmjs.org/download');
done();
@@ -90,7 +117,7 @@ describe('controllers/registry/module.test.js', function () {
request(app)
.get('/cnpmjs.org')
.set('If-None-Match', etag)
.expect(304, done)
.expect(304, done);
});
});
@@ -217,7 +244,7 @@ describe('controllers/registry/module.test.js', function () {
.expect(200, function (err, res) {
should.not.exist(err);
res.body.should.have.keys('_id', '_rev', 'name', 'description', 'versions', 'dist-tags',
'readme', 'maintainers', 'time', '_attachments');
'readme', 'maintainers', 'time', '_attachments', 'users');
res.body.versions.should.eql({});
res.body.time.should.eql({});
res.body['dist-tags'].should.eql({});
@@ -535,35 +562,7 @@ describe('controllers/registry/module.test.js', function () {
request(app)
.get('/cutter/download/cutter-0.0.2.tgz')
.expect('Location', 'http://qtestbucket.qiniudn.com/cutter/-/cutter-0.0.2.tgz')
.expect(302, done)
});
it('should download a file direct from nfs stream', function (done) {
var nfs = require('../../../common/nfs');
mm(nfs, 'downloadStream', thunkify(function (key, writeStream, options, callback) {
options.timeout.should.equal(600000);
nfs._client.download(key, {writeStream: writeStream, timeout: options.timeout}, callback);
}));
Module.__get__ = Module.get;
mm(Module, 'get', thunkify(function (name, version, callback) {
Module.__get__(name, version, function (err, info) {
info.package.dist.key = 'cutter/-/cutter-0.0.2.tgz';
callback(err, info);
});
}));
request(app)
.get('/cutter/download/cutter-0.0.2.tgz')
.expect('ETag', 'c61fde5e8c26d053574d0c722097029fd1bc963a')
.expect('Content-Type', 'application/octet-stream')
.expect('Content-Length', '3139')
.expect('Content-Disposition', 'attachment; filename="cutter-0.0.2.tgz"')
.expect(200)
.end(function (err, res) {
should.not.exist(err);
// TODO: why supertest change buffer to text?
// res.text.length.should.equal(3139);
done();
});
.expect(302, done);
});
});

View File

@@ -0,0 +1,37 @@
/**!
* cnpmjs.org - test/controllers/web/dist.test.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.cnpmjs.org)
*/
'use strict';
/**
* Module dependencies.
*/
var should = require('should');
var request = require('supertest');
var app = require('../../../servers/web');
describe('controllers/web/dist.test.js', function () {
before(function (done) {
app.listen(0, done);
});
after(function (done) {
app.close(done);
});
describe('GET /dist', function (done) {
it('should 302 to config.disturl', function (done) {
request(app)
.get('/dist')
.expect('Location', 'http://dist.u.qiniudn.com/')
.expect(302, done);
});
});
});

View File

@@ -78,9 +78,14 @@ describe('controllers/web/package.test.js', function () {
request(app)
.get('/package/cutter')
.expect(200)
.expect('content-encoding', 'gzip')
.expect(/<div id="package">/)
.expect(/<th>Maintainers<\/th>/)
.expect(/<th>Version<\/th>/, done);
.expect(/<th>Version<\/th>/, function (err, res) {
should.not.exist(err);
res.should.have.header('etag');
done();
});
});
it('should get 404', function (done) {
@@ -162,7 +167,7 @@ describe('controllers/web/package.test.js', function () {
url: 'http://opensource.org/licenses/MIT'
});
var p = {license: ['http://foo/MIT']};
p = {license: ['http://foo/MIT']};
pkg.setLicense(p);
p.license.should.have.keys('name', 'url');
p.license.should.eql({
@@ -170,7 +175,7 @@ describe('controllers/web/package.test.js', function () {
url: 'http://foo/MIT'
});
var p = {license: {name: 'mit', url: 'http://foo/mit'}};
p = {license: {name: 'mit', url: 'http://foo/mit'}};
pkg.setLicense(p);
p.license.should.have.keys('name', 'url');
p.license.should.eql({

View File

@@ -0,0 +1,63 @@
/**!
* cnpmjs.org - test/middleware/web_not_found.test.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var should = require('should');
var request = require('supertest');
var app = require('../../servers/web');
describe('middleware/web_not_found.test.js', function () {
before(function (done) {
app.listen(0, done);
});
after(function (done) {
app.close(done);
});
describe('web_not_found()', function () {
it('should redirect /byte to /package/byte', function (done) {
request(app)
.get('/byte')
.expect('Location', '/package/byte')
.expect(302, done);
});
it('should redirect /byte/ to /package/byte', function (done) {
request(app)
.get('/byte/')
.expect('Location', '/package/byte')
.expect(302, done);
});
it('should 404 /~byte', function (done) {
request(app)
.get('/~byte')
.expect(404, done);
});
it('should 200 /package/byte', function (done) {
request(app)
.get('/package/byte')
.expect(200, done);
});
it('should 404 /package/byte404', function (done) {
request(app)
.get('/package/byte404')
.expect(404, done);
});
});
});

View File

@@ -158,7 +158,7 @@ describe('proxy/module.test.js', function () {
should.not.exist(err);
data.package.readme.should.equal('test');
done();
})
});
});
});
});

View File

@@ -0,0 +1,64 @@
/**!
* cnpmjs.org - test/proxy/module_star.test.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var should = require('should');
var co = require('co');
var Star = require('../../proxy/module_star');
describe('proxy/module_star.test.js', function () {
before(function (done) {
co(function *() {
yield Star.remove('testmodule', 'fengmk2');
yield Star.remove('testmodule', 'mk1');
yield Star.remove('testmodule', 'mk2');
done();
})();
});
it('should add a star', function (done) {
co(function *() {
yield Star.add('testmodule', 'fengmk2');
// again should be ok
yield Star.add('testmodule', 'fengmk2');
yield Star.add('testmodule', 'fengmk2');
done();
})();
});
it('should get all star users', function (done) {
co(function *() {
yield Star.add('testmodule', 'fengmk2');
yield Star.add('testmodule', 'mk1');
yield Star.add('testmodule', 'mk2');
var rows = yield Star.listUsers('testmodule');
rows.should.containDeep(['fengmk2', 'mk1', 'mk2']);
done();
})();
});
it('should get user all star modules', function (done) {
co(function *() {
yield Star.add('testmodule', 'fengmk2');
yield Star.add('testmodule1', 'fengmk2');
yield Star.add('testmodule2', 'fengmk2');
var rows = yield Star.listUserModules('fengmk2');
rows.should.containDeep(['testmodule', 'testmodule1', 'testmodule2']);
done();
})();
});
});

View File

@@ -18,8 +18,8 @@ var SyncModuleWorker = require('../proxy/sync_module_worker');
var mysql = require('../common/mysql');
var Log = require('../proxy/module_log');
var name = process.argv[2] || 'address,pedding';
var names = name.split(',');
var names = process.argv[2] || 'byte';
names = names.split(',');
Log.create({
name: names[0],
@@ -30,8 +30,8 @@ Log.create({
name: names,
username: 'fengmk2',
concurrency: names.length,
noDep: true,
publish: true,
// noDep: true,
// publish: true,
});
worker.start();

View File

@@ -50,7 +50,7 @@ describe('sync/sync_all.js', function () {
mm.data(Module, 'listAllModuleNames', [{name: 'cnpmjs.org'}, {name: 'cutter'}]);
sync(function (err, data) {
should.not.exist(err);
data.successes.should.eql(['cnpm', 'cnpmjs.org', 'cutter']);
data.successes.should.eql(['cnpmjs.org', 'cutter']);
done();
});
});

View File

@@ -188,27 +188,27 @@
</td>
</tr>
<% if (package.users) {
var starredBy = package.starredBy
var l = starredBy.length
<% if (package.users.length > 0) {
var users = package.users
var l = users.length
%>
<tr>
<th>Starred by<%= l > 0 ? ' (' + l + ')' : '' %></th>
<th>Starred by (<%= l %>)</th>
<td>
<%
var max = 20
if (l > max) {
starredBy = starredBy.sort(function (a, b) {
users = users.sort(function (a, b) {
return Math.random() * 2 - 1
}).slice(0, max)
}
starredBy.forEach(function (user, i) {
users.forEach(function (user, i) {
if (i > 0) { %>, <% }
%><a href="/~<%= user %>"><%= user %></a><%
})
if (l > max) {
%><br><a href="/browse/star/<%= package.name %>">and
<%= (l-max) %> more</a><%
<%= (l - max) %> more</a><%
}
%>
</td>