Compare commits
110 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff50946cd3 | ||
|
|
3637eeefdd | ||
|
|
cecb41e6ea | ||
|
|
72b3240ef5 | ||
|
|
869d5681ce | ||
|
|
ff1bfd5acc | ||
|
|
7e3129c8b8 | ||
|
|
0618c732d7 | ||
|
|
9b34ab408c | ||
|
|
d990d3aa91 | ||
|
|
03ef728049 | ||
|
|
f00ed85d41 | ||
|
|
685af2a367 | ||
|
|
818f216fb4 | ||
|
|
1b47495565 | ||
|
|
0ab314f27c | ||
|
|
95076c8787 | ||
|
|
4e6eb0a9cc | ||
|
|
29f17dd5d1 | ||
|
|
3a48637ef1 | ||
|
|
e1029b005f | ||
|
|
66771dfc3b | ||
|
|
66f05a2f07 | ||
|
|
6660cabbb6 | ||
|
|
fbe1971957 | ||
|
|
d75c3877bb | ||
|
|
564ec488ea | ||
|
|
1559c16c3d | ||
|
|
2345536cbd | ||
|
|
89808e398b | ||
|
|
5ddf238c08 | ||
|
|
1ae193e306 | ||
|
|
9d511c326c | ||
|
|
78d7a77b0d | ||
|
|
0f494822bc | ||
|
|
211df84514 | ||
|
|
fee243726e | ||
|
|
52e7e6d069 | ||
|
|
551ae832e3 | ||
|
|
9af99f4af2 | ||
|
|
3e8ecda9e4 | ||
|
|
fb744176f8 | ||
|
|
5e1ab4356d | ||
|
|
5fb9a007f9 | ||
|
|
780a5aa158 | ||
|
|
ab2ff4ed9e | ||
|
|
2dad7553e6 | ||
|
|
acfa2e418b | ||
|
|
74101fda7a | ||
|
|
2ec1eec91c | ||
|
|
b3e966184a | ||
|
|
b09960858c | ||
|
|
84634af0c2 | ||
|
|
d60d7eaf2e | ||
|
|
6d6a994997 | ||
|
|
ab564c3b32 | ||
|
|
55b836388d | ||
|
|
3f45384b74 | ||
|
|
6fe2997fb5 | ||
|
|
cdd857ca2d | ||
|
|
c9e513350a | ||
|
|
7b2cbd6d1d | ||
|
|
90959ba34f | ||
|
|
0f6b6a2f2b | ||
|
|
666d98d86e | ||
|
|
e244efb153 | ||
|
|
67d824e5dc | ||
|
|
daf29f760d | ||
|
|
e5939d170f | ||
|
|
3526c9eff7 | ||
|
|
973889c73a | ||
|
|
70cd5f5cb6 | ||
|
|
ba6139f265 | ||
|
|
645e4913ba | ||
|
|
46eba8d03a | ||
|
|
9d2a649aac | ||
|
|
bf53537f00 | ||
|
|
1047e18732 | ||
|
|
2981a17b10 | ||
|
|
041416abf1 | ||
|
|
c117b67f3b | ||
|
|
bc2ac45588 | ||
|
|
95eba3fcfc | ||
|
|
e98b409f61 | ||
|
|
fedec47349 | ||
|
|
b63a565a37 | ||
|
|
67eb7fa380 | ||
|
|
a117886b71 | ||
|
|
152f6800da | ||
|
|
2f7e52f86f | ||
|
|
bb4b51c54f | ||
|
|
4c2303e4fa | ||
|
|
b70201db79 | ||
|
|
dd095147b0 | ||
|
|
08c4445e78 | ||
|
|
b892ad8185 | ||
|
|
a8ee1e43bf | ||
|
|
dd84674b98 | ||
|
|
ccec2cb45b | ||
|
|
f3a4500191 | ||
|
|
161a860f74 | ||
|
|
f58e8a24b0 | ||
|
|
9bc6a6dd74 | ||
|
|
c50364807d | ||
|
|
b3e3ecfc3a | ||
|
|
637b5fe785 | ||
|
|
6eed7b9002 | ||
|
|
899becb448 | ||
|
|
7e982ca877 | ||
|
|
26156253c7 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -20,3 +20,6 @@ backup/*.json
|
||||
backup/*.gz
|
||||
docs/web/history.md
|
||||
view/web/_layout.html
|
||||
bin/mysql.js
|
||||
bin/test.sql
|
||||
coverage/
|
||||
|
||||
@@ -9,3 +9,5 @@ public/dist/
|
||||
backup/*.json
|
||||
backup/*.gz
|
||||
view/web/_layout.html
|
||||
bin/mysql.js
|
||||
coverage/
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- '0.10'
|
||||
install: make install
|
||||
script: make test-coveralls
|
||||
- '0.11'
|
||||
|
||||
7
AUTHORS
7
AUTHORS
@@ -1,7 +1,8 @@
|
||||
# Ordered by date of first contribution.
|
||||
# Auto-generated by 'contributors' on Fri, 10 Jan 2014 02:56:32 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)
|
||||
dead_horse <dead_horse@qq.com> (https://github.com/dead-horse)
|
||||
AlsoTang <alsotang@gmail.com> (https://github.com/alsotang)
|
||||
dead-horse <dead_horse@qq.com> (https://github.com/dead-horse)
|
||||
alsotang <alsotang@gmail.com> (https://github.com/alsotang)
|
||||
4simple <wondger@qq.com> (https://github.com/4simple)
|
||||
|
||||
140
History.md
140
History.md
@@ -1,4 +1,144 @@
|
||||
|
||||
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
|
||||
==================
|
||||
|
||||
* add etag fixed #224
|
||||
* travis ci install on source npm
|
||||
|
||||
0.3.0 / 2014-02-27
|
||||
==================
|
||||
|
||||
* fix typo and dont sync not exists pkgs
|
||||
* use koa-middlewares
|
||||
* fix signed cookie not work on npm@1.3.25; node --harmony-generators
|
||||
* fix opensearch test case
|
||||
* update koa bodyparser
|
||||
* logger.error(err) should send err stack email notice
|
||||
* json body parse limit and bug fix.
|
||||
* fix sync 404 reason not clear
|
||||
* all controllers to koa
|
||||
* controller/web/user.js to koa
|
||||
* change web connect to koa
|
||||
* use outputError
|
||||
* use yield exports.addPackageAndDist.call(this, next);
|
||||
* add end() when ws write end
|
||||
* fix yield coWrite
|
||||
* fix all the test of registry module.test.js
|
||||
* convert registry/module.js to koa type
|
||||
* fix auth middleware
|
||||
* finish registry user controller koa and update mm to support thunkify. fixed #196
|
||||
* change controllers/user.js to koa
|
||||
* thunkify all proxy
|
||||
* convert all middlewares to koa type
|
||||
* change regsitry sync to koa
|
||||
* addd koa-jsonp, koa-bodyparser, fix / controller
|
||||
* first koa run registry home page /
|
||||
* Merge pull request #212 from cnpm/fix-sync-404
|
||||
* return friendly 404 reason
|
||||
* Merge pull request #211 from cnpm/bug-fix
|
||||
* override json limit to default 10mb. fixed #209
|
||||
* fix #210 addPackageAndDist package version detect bug
|
||||
|
||||
0.2.27 / 2014-02-19
|
||||
==================
|
||||
|
||||
* support json result in search, fixed #189
|
||||
|
||||
0.2.26 / 2014-02-19
|
||||
==================
|
||||
|
||||
* npm publish also need to add deps
|
||||
|
||||
0.2.25 / 2014-02-19
|
||||
==================
|
||||
|
||||
* max handle number of package.json `dependencies` property
|
||||
* Dependents support. fixed #190
|
||||
|
||||
0.2.24 / 2014-02-13
|
||||
==================
|
||||
|
||||
* fix if delete all the versions
|
||||
* refactor remove module, fixed #186
|
||||
|
||||
0.2.23 / 2014-01-26
|
||||
==================
|
||||
|
||||
* system admin can add, publish, remove the packages. fixed #176
|
||||
|
||||
0.2.22 / 2014-01-26
|
||||
==================
|
||||
|
||||
* add keyword and search support keyword. #181
|
||||
|
||||
0.2.21 / 2014-01-24
|
||||
==================
|
||||
|
||||
* refactor code styles on package.html
|
||||
* nav-tabs e.preventDefault
|
||||
* Show registry server error response. fixed #178
|
||||
* nav-tabs for package.html (@4simple)
|
||||
|
||||
0.2.20 / 2014-01-23
|
||||
==================
|
||||
|
||||
* hotfix sync missing dependencies and readmes
|
||||
* fix sync readme error, fixed #174
|
||||
* add updateReadme in module
|
||||
|
||||
0.2.19 / 2014-01-22
|
||||
==================
|
||||
|
||||
* npm install no need to check authorization header. fixed #171
|
||||
|
||||
0.2.18 / 2014-01-20
|
||||
==================
|
||||
|
||||
* Support gitlab git url to display and click. fixed #160
|
||||
* fix redis crash
|
||||
|
||||
0.2.17 / 2014-01-17
|
||||
==================
|
||||
|
||||
* custom logo url
|
||||
* hotfix layout bug
|
||||
|
||||
0.2.16 / 2014-01-16
|
||||
==================
|
||||
|
||||
* fix publish-time bug
|
||||
|
||||
0.2.15 / 2014-01-16
|
||||
==================
|
||||
|
||||
* add publish_time to debug
|
||||
|
||||
0.2.14 / 2014-01-16
|
||||
==================
|
||||
|
||||
|
||||
37
Makefile
37
Makefile
@@ -4,34 +4,33 @@ 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 \
|
||||
--cache=${HOME}/.npm/.cache/cnpm --disturl=http://dist.u.qiniudn.com
|
||||
|
||||
test: install
|
||||
@NODE_ENV=test ./node_modules/mocha/bin/mocha \
|
||||
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
|
||||
autod:
|
||||
@./node_modules/.bin/autod -w -e public,view,docs,backup
|
||||
@$(MAKE) install
|
||||
|
||||
|
||||
28
README.md
28
README.md
@@ -1,7 +1,7 @@
|
||||
cnpmjs.org
|
||||
=======
|
||||
|
||||
[](http://travis-ci.org/cnpm/cnpmjs.org) [](https://coveralls.io/r/cnpm/cnpmjs.org)[](https://gemnasium.com/cnpm/cnpmjs.org)
|
||||
[](http://travis-ci.org/cnpm/cnpmjs.org) [](https://coveralls.io/r/cnpm/cnpmjs.org) [](https://gemnasium.com/cnpm/cnpmjs.org)
|
||||
|
||||
[](https://nodei.co/npm/cnpmjs.org/)
|
||||
|
||||
@@ -9,7 +9,7 @@ cnpmjs.org
|
||||
|
||||
## What is this?
|
||||
|
||||
Private npm registry and web for Enterprise, base on MySQL and [Simple Store Service](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide).
|
||||
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).
|
||||
|
||||
@@ -19,29 +19,35 @@ Private npm registry and web for Enterprise, base on MySQL and [Simple Store Ser
|
||||
## Install
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
$ npm install --registry=http://r.cnpmjs.org --disturl=http://dist.u.qiniudn.com
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
$ node dispatch.js
|
||||
$ node --harmony-generators dispatch.js
|
||||
```
|
||||
|
||||
## Guide
|
||||
|
||||
* [How to deploy cnpmjs.org](https://github.com/cnpm/cnpmjs.org/wiki/Deploy)
|
||||
* [NFS guide](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide)
|
||||
|
||||
## Authors
|
||||
|
||||
```bash
|
||||
$ git summary
|
||||
|
||||
project : cnpmjs.org
|
||||
repo age : 5 weeks
|
||||
active : 98 days
|
||||
commits : 239
|
||||
files : 85
|
||||
repo age : 3 months
|
||||
active : 145 days
|
||||
commits : 366
|
||||
files : 94
|
||||
authors :
|
||||
140 fengmk2 58.6%
|
||||
98 dead_horse 41.0%
|
||||
1 Alsotang 0.4%
|
||||
217 fengmk2 59.3%
|
||||
146 dead_horse 39.9%
|
||||
2 4simple 0.5%
|
||||
1 Alsotang 0.3%
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
@@ -7,7 +7,7 @@ export NODE_ENV='production'
|
||||
ulimit -c unlimited
|
||||
|
||||
cd `dirname $0`/..
|
||||
NODEJS=node
|
||||
NODEJS='node --harmony-generators'
|
||||
BASE_HOME=`pwd`
|
||||
PROJECT_NAME=`basename ${BASE_HOME}`
|
||||
STDOUT_LOG=`$NODEJS -e "console.log(require('path').join(require('$BASE_HOME/config').logdir, 'nodejs_stdout.log'));\
|
||||
|
||||
74
bin/restore_module_deps.js
Normal file
74
bin/restore_module_deps.js
Normal file
@@ -0,0 +1,74 @@
|
||||
/**!
|
||||
* cnpmjs.org - bin/restore_module_deps.js
|
||||
*
|
||||
* Copyright(c) 2014
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var mysql = require('../common/mysql');
|
||||
var Module = require('../proxy/module');
|
||||
var ModuleDeps = require('../proxy/module_deps');
|
||||
|
||||
var addCount = 0;
|
||||
|
||||
function restore(id, callback) {
|
||||
var sql = 'SELECT id, name, package FROM module WHERE id > ? ORDER BY id ASC LIMIT 1000';
|
||||
mysql.query(sql, [id], function (err, rows) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (rows.length === 0) {
|
||||
return callback(null, []);
|
||||
}
|
||||
|
||||
console.log('[%s] got %d rows', id, rows.length);
|
||||
|
||||
rows.forEach(function (r) {
|
||||
Module.parseRow(r);
|
||||
if (!r.package) {
|
||||
return;
|
||||
}
|
||||
var deps = Object.keys(r.package.dependencies || {});
|
||||
if (!Array.isArray(deps) || !deps.length) {
|
||||
return;
|
||||
}
|
||||
deps.forEach(function (dep) {
|
||||
ModuleDeps.add(dep, r.name, function (err) {
|
||||
// console.log('[%s] add %s <= %s, error: %s', id, dep, r.name, err);
|
||||
});
|
||||
});
|
||||
addCount += deps.length;
|
||||
});
|
||||
setTimeout(function () {
|
||||
console.log('[%s] add %d relations', id, addCount);
|
||||
callback(null, rows);
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
var id = 0;
|
||||
function run() {
|
||||
restore(id, function (err, rows) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
if (rows.length === 0) {
|
||||
console.log('finished, last id: %s, exit.', id);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
id = rows[rows.length - 1].id;
|
||||
run();
|
||||
});
|
||||
}
|
||||
|
||||
run();
|
||||
@@ -1,7 +1,12 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - common/logger.js
|
||||
* Copyright(c) 2013
|
||||
* Author: dead_horse <undefined>
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -10,11 +15,12 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var config = require('../config');
|
||||
var util = require('util');
|
||||
var moment = require('moment');
|
||||
var logstream = require('logfilestream');
|
||||
var ms = require('ms');
|
||||
var config = require('../config');
|
||||
var mail = require('./mail');
|
||||
|
||||
var isTEST = process.env.NODE_ENV === 'test';
|
||||
var ONE_DAY = ms('1d');
|
||||
@@ -29,7 +35,9 @@ levels.forEach(function (catetory) {
|
||||
var stream = logstream(options);
|
||||
function write(msg) {
|
||||
var time = moment().format('YYYY-MM-DD HH:mm:ss.SSS');
|
||||
var subject = null;
|
||||
if (msg instanceof Error) {
|
||||
subject = msg.name;
|
||||
var err = {
|
||||
name: msg.name,
|
||||
code: msg.code,
|
||||
@@ -57,12 +65,21 @@ levels.forEach(function (catetory) {
|
||||
} else {
|
||||
msg = time + ' ' + util.format.apply(util, arguments) + '\n';
|
||||
}
|
||||
|
||||
if (!isTEST) {
|
||||
stream.write(msg);
|
||||
if (config.debug) {
|
||||
var level = catetory;
|
||||
console.log('[' + level + '] ' + msg);
|
||||
} else {
|
||||
stream.write(msg);
|
||||
if (catetory === 'error' && subject) {
|
||||
// send error email
|
||||
var to = [];
|
||||
for (var name in config.admins) {
|
||||
to.push(config.admins[name]);
|
||||
}
|
||||
mail.error(to, subject, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - common/mail.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
|
||||
@@ -35,7 +35,9 @@ var pool = mysql.createPool({
|
||||
exports.pool = pool;
|
||||
|
||||
exports.query = function (sql, values, cb) {
|
||||
pool.query(sql, values, cb);
|
||||
pool.query(sql, values, function (err, rows) {
|
||||
cb(err, rows);
|
||||
});
|
||||
};
|
||||
|
||||
exports.queryOne = function (sql, values, cb) {
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var qn = require('qn');
|
||||
var config = require('../config');
|
||||
|
||||
var client = qn.create(config.qn);
|
||||
|
||||
exports._client = client;
|
||||
@@ -61,3 +61,5 @@ exports.url = function (key) {
|
||||
exports.remove = function (key, callback) {
|
||||
client.delete(key, callback);
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
@@ -15,27 +15,18 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var connect = require('connect');
|
||||
var RedisStore = require('connect-redis')(connect);
|
||||
var middlewares = require('koa-middlewares');
|
||||
var config = require('../config');
|
||||
|
||||
var session;
|
||||
var key = 'AuthSession';
|
||||
var cookie = { path: '/', httpOnly: true, maxAge: 3600000 * 24 * 30 };
|
||||
var cookie = { path: '/', httpOnly: true, maxAge: 3600000 * 24 * 365, signed: false };
|
||||
var options = {
|
||||
key: key,
|
||||
cookie: cookie,
|
||||
};
|
||||
|
||||
if (config.debug) {
|
||||
session = connect.cookieSession({
|
||||
secret: config.sessionSecret,
|
||||
key: key,
|
||||
cookie: cookie
|
||||
});
|
||||
} else {
|
||||
session = connect.session({
|
||||
key: key,
|
||||
secret: config.sessionSecret,
|
||||
store: config.sessionStore || new RedisStore(config.redis),
|
||||
cookie: cookie,
|
||||
});
|
||||
if (!config.debug) {
|
||||
options.store = config.sessionStore || middlewares.RedisStore(config.redis);
|
||||
}
|
||||
|
||||
module.exports = session;
|
||||
module.exports = middlewares.session(options);
|
||||
|
||||
@@ -51,6 +51,7 @@ var config = {
|
||||
port: 19533,
|
||||
pass: 'cnpmjs_dev'
|
||||
},
|
||||
jsonLimit: '10mb', // max request json body size
|
||||
uploadDir: path.join(root, 'public', 'dist'),
|
||||
// qiniu cdn: http://www.qiniu.com/, it free for dev.
|
||||
qn: {
|
||||
@@ -71,6 +72,7 @@ var config = {
|
||||
debug: false
|
||||
},
|
||||
|
||||
logoURL: 'http://ww4.sinaimg.cn/large/69c1d4acgw1ebfly5kjlij208202oglr.jpg',
|
||||
registryHost: 'r.cnpmjs.org',
|
||||
customFooter: '', // you can add copyright and site total script html here
|
||||
npmClientName: 'cnpm', // use `${name} install package`
|
||||
@@ -78,8 +80,8 @@ var config = {
|
||||
sourceNpmRegistry: 'http://registry.npmjs.org',
|
||||
enablePrivate: true, // enable private mode, only admin can publish, other use just can sync package from source npm
|
||||
admins: {
|
||||
admin: 'admin@cnpmjs.org',
|
||||
fengmk2: 'fengmk2@gmail.com',
|
||||
admin: 'admin@cnpmjs.org',
|
||||
dead_horse: 'dead_horse@qq.com',
|
||||
cnpmjstest10: 'cnpmjstest10@cnpmjs.org',
|
||||
},
|
||||
@@ -87,6 +89,7 @@ var config = {
|
||||
backupFilePrefix: '/cnpm/backup/', // backup filepath prefix
|
||||
syncModel: 'none', // 'none', 'all', 'exist'
|
||||
syncConcurrency: 1,
|
||||
maxDependencies: 200, // max handle number of package.json `dependencies` property
|
||||
};
|
||||
|
||||
// load config/config.js, everything in config.js will cover the same key in index.js
|
||||
|
||||
@@ -14,10 +14,15 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var moment = require('moment');
|
||||
var DownloadTotal = require('../proxy/download');
|
||||
|
||||
exports.total = function (name, callback) {
|
||||
if (typeof name === 'function') {
|
||||
callback = name;
|
||||
name = null;
|
||||
}
|
||||
var end = moment();
|
||||
var start = end.clone().subtract('months', 1).startOf('month');
|
||||
var lastday = end.clone().subtract('days', 1).format('YYYY-MM-DD');
|
||||
@@ -74,3 +79,5 @@ exports.total = function (name, callback) {
|
||||
|
||||
DownloadTotal[method].apply(DownloadTotal, args);
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/user.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -18,30 +18,24 @@
|
||||
var debug = require('debug')('cnpmjs.org:controllers:registry');
|
||||
var logger = require('../../common/logger');
|
||||
var User = require('../../proxy/user');
|
||||
var eventproxy = require('eventproxy');
|
||||
|
||||
exports.show = function (req, res, next) {
|
||||
var name = req.params.name;
|
||||
User.get(name, function (err, row) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (!row) {
|
||||
return next();
|
||||
}
|
||||
|
||||
res.setHeader('etag', '"' + row.rev + '"');
|
||||
var data = {
|
||||
_id: 'org.couchdb.user:' + row.name,
|
||||
_rev: row.rev,
|
||||
name: row.name,
|
||||
email: row.email,
|
||||
type: 'user',
|
||||
roles: [],
|
||||
date: row.gmt_modified,
|
||||
};
|
||||
res.json(data);
|
||||
});
|
||||
exports.show = function *(next) {
|
||||
var name = this.params.name;
|
||||
var user = yield User.get(name);
|
||||
if (!user) {
|
||||
return yield next;
|
||||
}
|
||||
this.etag = '"' + user.rev + '"';
|
||||
var data = {
|
||||
_id: 'org.couchdb.user:' + user.name,
|
||||
_rev: user.rev,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
type: 'user',
|
||||
roles: [],
|
||||
date: user.gmt_modified,
|
||||
};
|
||||
this.body = data;
|
||||
};
|
||||
|
||||
// json:
|
||||
@@ -53,110 +47,109 @@ exports.show = function (req, res, next) {
|
||||
// type: 'user',
|
||||
// roles: [],
|
||||
// date: '2013-12-04T12:56:13.714Z' } }
|
||||
exports.add = function (req, res, next) {
|
||||
var name = req.params.name;
|
||||
var body = req.body || {};
|
||||
exports.add = function *() {
|
||||
var name = this.params.name;
|
||||
var body = this.request.body || {};
|
||||
var user = {
|
||||
name: body.name,
|
||||
salt: body.salt,
|
||||
password_sha: body.password_sha,
|
||||
email: body.email,
|
||||
ip: req.socket && req.socket.remoteAddress || '0.0.0.0',
|
||||
ip: this.ip || '0.0.0.0',
|
||||
// roles: body.roles || [],
|
||||
};
|
||||
|
||||
if (!user.name || !user.salt || !user.password_sha || !user.email) {
|
||||
return res.json(422, {
|
||||
this.status = 422;
|
||||
this.body = {
|
||||
error: 'paramError',
|
||||
reason: 'params missing'
|
||||
});
|
||||
};
|
||||
return;
|
||||
}
|
||||
debug('add user: %j', user);
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(next);
|
||||
|
||||
User.get(name, ep.doneLater(function (row) {
|
||||
if (row) {
|
||||
return res.json(409, {
|
||||
error: 'conflict',
|
||||
reason: 'Document update conflict.'
|
||||
});
|
||||
}
|
||||
User.add(user, ep.done('add'));
|
||||
}));
|
||||
var existUser = yield User.get(name);
|
||||
if (existUser) {
|
||||
this.status = 409;
|
||||
this.body = {
|
||||
error: 'conflict',
|
||||
reason: 'Document update conflict.'
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
ep.once('add', function (result) {
|
||||
res.setHeader('etag', '"' + result.rev + '"');
|
||||
// location: 'http://registry.npmjs.org/_users/org.couchdb.user:cnpmjstest1',
|
||||
res.json(201, {
|
||||
ok: true,
|
||||
id: 'org.couchdb.user:' + name,
|
||||
rev: result.rev
|
||||
});
|
||||
});
|
||||
var result = yield User.add(user);
|
||||
this.etag = '"' + result.rev + '"';
|
||||
this.status = 201;
|
||||
this.body = {
|
||||
ok: true,
|
||||
id: 'org.couchdb.user:' + name,
|
||||
rev: result.rev
|
||||
};
|
||||
};
|
||||
|
||||
exports.authSession = function (req, res, next) {
|
||||
exports.authSession = function *() {
|
||||
// body: {"name":"foo","password":"****"}
|
||||
var body = req.body || {};
|
||||
var body = this.request.body || {};
|
||||
var name = body.name;
|
||||
var password = body.password;
|
||||
User.auth(name, password, function (err, user) {
|
||||
debug('authSession %s: %j', name, user);
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (!user) {
|
||||
return res.json(401, {ok: false, name: null, roles: []});
|
||||
}
|
||||
var user = yield User.auth(name, password);
|
||||
debug('authSession %s: %j', name, user);
|
||||
|
||||
req.session.name = user.name;
|
||||
res.json(200, {ok: true, name: user.name, roles: []});
|
||||
});
|
||||
if (!user) {
|
||||
this.status = 401;
|
||||
this.body = {ok: false, name: null, roles: []};
|
||||
return;
|
||||
}
|
||||
|
||||
this.session.name = user.name;
|
||||
this.body = {ok: true, name: user.name, roles: []};
|
||||
};
|
||||
|
||||
exports.update = function (req, res, next) {
|
||||
var name = req.params.name;
|
||||
var rev = req.params.rev;
|
||||
exports.update = function *(next) {
|
||||
var name = this.params.name;
|
||||
var rev = this.params.rev;
|
||||
if (!name || !rev) {
|
||||
return next();
|
||||
return yield next;
|
||||
}
|
||||
|
||||
debug('update: %s, rev: %s, session.name: %s', name, rev, req.session.name);
|
||||
debug('update: %s, rev: %s, session.name: %s', name, rev, this.session.name);
|
||||
|
||||
if (name !== req.session.name) {
|
||||
if (name !== this.session.name) {
|
||||
// must authSession first
|
||||
res.statusCode = 401;
|
||||
return res.json({
|
||||
this.status = 401;
|
||||
this.body = {
|
||||
error: 'unauthorized',
|
||||
reason: 'Name is incorrect.'
|
||||
});
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
var body = req.body || {};
|
||||
var body = this.request.body || {};
|
||||
var user = {
|
||||
name: body.name,
|
||||
salt: body.salt,
|
||||
password_sha: body.password_sha,
|
||||
email: body.email,
|
||||
ip: req.socket && req.socket.remoteAddress || '0.0.0.0',
|
||||
ip: this.ip || '0.0.0.0',
|
||||
rev: body.rev || body._rev,
|
||||
// roles: body.roles || [],
|
||||
};
|
||||
User.update(user, function (err, result) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
//check rev error
|
||||
if (!result) {
|
||||
return res.json(409, {
|
||||
error: 'conflict',
|
||||
reason: 'Document update conflict.'
|
||||
});
|
||||
}
|
||||
res.json(201, {
|
||||
ok: true,
|
||||
id: 'org.couchdb.user:' + user.name,
|
||||
rev: result.rev
|
||||
});
|
||||
});
|
||||
var result = yield User.update(user);
|
||||
if (!result) {
|
||||
this.status = 409;
|
||||
this.body = {
|
||||
error: 'conflict',
|
||||
reason: 'Document update conflict.'
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
this.status = 201;
|
||||
this.body = {
|
||||
ok: true,
|
||||
id: 'org.couchdb.user:' + user.name,
|
||||
rev: result.rev
|
||||
};
|
||||
};
|
||||
|
||||
@@ -17,48 +17,60 @@
|
||||
var Log = require('../proxy/module_log');
|
||||
var SyncModuleWorker = require('../proxy/sync_module_worker');
|
||||
|
||||
exports.sync = function (req, res, next) {
|
||||
var username = req.session.name || 'anonymous';
|
||||
var name = req.params.name;
|
||||
var publish = req.query.publish === 'true';
|
||||
var noDep = req.query.nodeps === 'true';
|
||||
if (publish && !req.session.isAdmin) {
|
||||
return res.json(403, {
|
||||
exports.sync = function *() {
|
||||
var username = this.session.name || 'anonymous';
|
||||
var name = this.params.name;
|
||||
var publish = this.query.publish === 'true';
|
||||
var noDep = this.query.nodeps === 'true';
|
||||
if (publish && !this.session.isAdmin) {
|
||||
this.status = 403;
|
||||
this.body = {
|
||||
error: 'no_perms',
|
||||
reason: 'Only admin can publish'
|
||||
});
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
var options = {
|
||||
publish: publish,
|
||||
noDep: noDep,
|
||||
};
|
||||
SyncModuleWorker.sync(name, username, options, function (err, result) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (!result.ok) {
|
||||
return res.json(result.statusCode, result.pkg);
|
||||
}
|
||||
res.json(201, {
|
||||
ok: true,
|
||||
logId: result.logId
|
||||
});
|
||||
});
|
||||
|
||||
var result = yield SyncModuleWorker.sync(name, username, options);
|
||||
|
||||
// friendly 404 reason info
|
||||
if (result.staticCache === 404) {
|
||||
this.status = 404;
|
||||
this.body = {
|
||||
ok: false,
|
||||
reason: 'can not found ' + name + ' in the source registry'
|
||||
};
|
||||
return;
|
||||
}
|
||||
if (!result.ok) {
|
||||
this.status = result.statusCode;
|
||||
this.body = result.pkg;
|
||||
return;
|
||||
}
|
||||
this.status = 201;
|
||||
this.body = {
|
||||
ok: true,
|
||||
logId: result.logId
|
||||
};
|
||||
};
|
||||
|
||||
exports.getSyncLog = function (req, res, next) {
|
||||
var logId = req.params.id;
|
||||
var name = req.params.name;
|
||||
var offset = Number(req.query.offset) || 0;
|
||||
Log.get(logId, function (err, row) {
|
||||
if (err || !row) {
|
||||
return next(err);
|
||||
}
|
||||
var log = row.log.trim();
|
||||
if (offset > 0) {
|
||||
log = log.split('\n').slice(offset).join('\n');
|
||||
}
|
||||
res.json(200, {ok: true, log: log});
|
||||
});
|
||||
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;
|
||||
}
|
||||
|
||||
var log = row.log.trim();
|
||||
if (offset > 0) {
|
||||
log = log.split('\n').slice(offset).join('\n');
|
||||
}
|
||||
this.body = {ok: true, log: log};
|
||||
};
|
||||
|
||||
@@ -16,31 +16,25 @@
|
||||
*/
|
||||
|
||||
var microtime = require('microtime');
|
||||
var eventproxy = require('eventproxy');
|
||||
var Total = require('../proxy/total');
|
||||
var down = require('./download');
|
||||
var Download = require('./download');
|
||||
var version = require('../package.json').version;
|
||||
var config = require('../config');
|
||||
|
||||
var startTime = '' + microtime.now();
|
||||
|
||||
exports.show = function (req, res, next) {
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(next);
|
||||
exports.show = function *() {
|
||||
var r = yield [Total.get(), Download.total()];
|
||||
var total = r[0];
|
||||
var download = r[1];
|
||||
|
||||
Total.get(ep.done('total'));
|
||||
down.total(null, ep.done('download'));
|
||||
ep.all('total', 'download', function (total, download) {
|
||||
total.download = download;
|
||||
total.db_name = 'registry';
|
||||
total.instance_start_time = startTime;
|
||||
total.node_version = process.version;
|
||||
total.app_version = version;
|
||||
total.donate = 'https://me.alipay.com/imk2';
|
||||
total.sync_model = config.syncModel;
|
||||
if (req.query.callback) {
|
||||
return res.jsonp(total, req.query.callback);
|
||||
}
|
||||
res.json(total);
|
||||
});
|
||||
total.download = download;
|
||||
total.db_name = 'registry';
|
||||
total.instance_start_time = startTime;
|
||||
total.node_version = process.version;
|
||||
total.app_version = version;
|
||||
total.donate = 'https://me.alipay.com/imk2';
|
||||
total.sync_model = config.syncModel;
|
||||
|
||||
this.body = total;
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var giturl = require('giturl');
|
||||
var moment = require('moment');
|
||||
var eventproxy = require('eventproxy');
|
||||
var semver = require('semver');
|
||||
@@ -25,129 +26,148 @@ var Module = require('../../proxy/module');
|
||||
var down = require('../download');
|
||||
var sync = require('../sync');
|
||||
var Log = require('../../proxy/module_log');
|
||||
var ModuleDeps = require('../../proxy/module_deps');
|
||||
var setDownloadURL = require('../../lib/common').setDownloadURL;
|
||||
|
||||
exports.display = function (req, res, next) {
|
||||
var params = req.params;
|
||||
exports.display = function *(next) {
|
||||
var params = this.params;
|
||||
var name = params.name;
|
||||
var tag = params.version;
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(next);
|
||||
|
||||
if (tag) {
|
||||
var version = semver.valid(tag);
|
||||
if (version) {
|
||||
Module.get(name, version, ep.done('pkg'));
|
||||
} else {
|
||||
Module.getByTag(name, tag, ep.done('pkg'));
|
||||
}
|
||||
var getPackageMethod;
|
||||
var getPackageArgs;
|
||||
var version = semver.valid(tag || '');
|
||||
if (version) {
|
||||
getPackageMethod = 'get';
|
||||
getPackageArgs = [name, version];
|
||||
} else {
|
||||
Module.getByTag(name, 'latest', ep.done('pkg'));
|
||||
getPackageMethod = 'getByTag';
|
||||
getPackageArgs = [name, tag || 'latest'];
|
||||
}
|
||||
var r = yield [
|
||||
Module[getPackageMethod].apply(Module, getPackageArgs),
|
||||
down.total(name),
|
||||
ModuleDeps.list(name)
|
||||
];
|
||||
var pkg = r[0];
|
||||
var download = r[1];
|
||||
var dependents = (r[2] || []).map(function (item) {
|
||||
return item.deps;
|
||||
});
|
||||
|
||||
if (!pkg || !pkg.package) {
|
||||
return yield next;
|
||||
}
|
||||
|
||||
down.total(name, ep.done('download'));
|
||||
pkg.package.fromNow = moment(pkg.publish_time).fromNow();
|
||||
pkg = pkg.package;
|
||||
pkg.readme = marked(pkg.readme || '');
|
||||
if (!pkg.readme) {
|
||||
pkg.readme = pkg.description || '';
|
||||
}
|
||||
|
||||
ep.all('pkg', 'download', function (pkg, download) {
|
||||
if (!pkg || !pkg.package) {
|
||||
return next();
|
||||
}
|
||||
|
||||
pkg.package.fromNow = moment(pkg.publish_time).fromNow();
|
||||
pkg = pkg.package;
|
||||
pkg.readme = marked(pkg.readme || '');
|
||||
if (pkg.maintainers) {
|
||||
for (var i = 0; i < pkg.maintainers.length; i++) {
|
||||
var maintainer = pkg.maintainers[i];
|
||||
if (maintainer.email) {
|
||||
maintainer.gravatar = gravatar.url(maintainer.email, {s: '50', d: 'retro'}, false);
|
||||
}
|
||||
if (pkg.maintainers) {
|
||||
for (var i = 0; i < pkg.maintainers.length; i++) {
|
||||
var maintainer = pkg.maintainers[i];
|
||||
if (maintainer.email) {
|
||||
maintainer.gravatar = gravatar.url(maintainer.email, {s: '50', d: 'retro'}, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pkg.contributors) {
|
||||
// registry.cnpmjs.org/compressible
|
||||
if (!Array.isArray(pkg.contributors)) {
|
||||
pkg.contributors = [pkg.contributors];
|
||||
if (pkg.contributors) {
|
||||
// registry.cnpmjs.org/compressible
|
||||
if (!Array.isArray(pkg.contributors)) {
|
||||
pkg.contributors = [pkg.contributors];
|
||||
}
|
||||
for (var i = 0; i < pkg.contributors.length; i++) {
|
||||
var contributor = pkg.contributors[i];
|
||||
if (contributor.email) {
|
||||
contributor.gravatar = gravatar.url(contributor.email, {s: '50', d: 'retro'}, false);
|
||||
}
|
||||
for (var i = 0; i < pkg.contributors.length; i++) {
|
||||
var contributor = pkg.contributors[i];
|
||||
if (contributor.email) {
|
||||
contributor.gravatar = gravatar.url(contributor.email, {s: '50', d: 'retro'}, false);
|
||||
}
|
||||
if (config.packagePageContributorSearch || !contributor.url) {
|
||||
contributor.url = '/~' + encodeURIComponent(contributor.name);
|
||||
}
|
||||
if (config.packagePageContributorSearch || !contributor.url) {
|
||||
contributor.url = '/~' + encodeURIComponent(contributor.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setLicense(pkg);
|
||||
if (pkg.repository && pkg.repository.url) {
|
||||
pkg.repository.weburl = giturl.parse(pkg.repository.url) || pkg.repository.url;
|
||||
}
|
||||
|
||||
for (var k in download) {
|
||||
download[k] = humanize(download[k]);
|
||||
}
|
||||
setLicense(pkg);
|
||||
|
||||
setDownloadURL(pkg, req, config.registryHost);
|
||||
for (var k in download) {
|
||||
download[k] = humanize(download[k]);
|
||||
}
|
||||
setDownloadURL(pkg, this, config.registryHost);
|
||||
|
||||
res.render('package', {
|
||||
title: 'Package - ' + pkg.name,
|
||||
package: pkg,
|
||||
download: download
|
||||
});
|
||||
pkg.dependents = dependents;
|
||||
|
||||
yield this.render('package', {
|
||||
title: 'Package - ' + pkg.name,
|
||||
package: pkg,
|
||||
download: download
|
||||
});
|
||||
};
|
||||
|
||||
exports.search = function (req, res, next) {
|
||||
var params = req.params;
|
||||
var word = req.params.word;
|
||||
Module.search(word, function (err, packages) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
res.render('search', {
|
||||
title: 'Keyword - ' + word,
|
||||
exports.search = function *(next) {
|
||||
var params = this.params;
|
||||
var word = params.word;
|
||||
var result = yield Module.search(word);
|
||||
// return a json result
|
||||
if (this.query && this.query.type === 'json') {
|
||||
this.body = {
|
||||
keyword: word,
|
||||
packages: packages || []
|
||||
});
|
||||
packages: result.searchMatchs,
|
||||
keywords: result.keywordMatchs
|
||||
};
|
||||
this.charset = 'utf-8';
|
||||
return;
|
||||
}
|
||||
|
||||
yield this.render('search', {
|
||||
title: 'Keyword - ' + word,
|
||||
keyword: word,
|
||||
packages: result.searchMatchs,
|
||||
keywords: result.keywordMatchs,
|
||||
});
|
||||
};
|
||||
|
||||
exports.rangeSearch = function (req, res, next) {
|
||||
var startKey = req.query.startkey || '';
|
||||
exports.rangeSearch = function *(next) {
|
||||
var startKey = this.query.startkey || '';
|
||||
if (startKey[0] === '"') {
|
||||
startKey = startKey.substring(1);
|
||||
}
|
||||
if (startKey[startKey.length - 1] === '"') {
|
||||
startKey = startKey.substring(0, startKey.length - 1);
|
||||
}
|
||||
var limit = Number(req.query.limit) || 20;
|
||||
Module.search(startKey, {limit: limit}, function (err, packages) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
var limit = Number(this.query.limit) || 20;
|
||||
var result = yield Module.search(startKey, {limit: limit});
|
||||
|
||||
var rows = [];
|
||||
for (var i = 0; i < packages.length; i++) {
|
||||
var p = packages[i];
|
||||
var row = {
|
||||
key: p.name,
|
||||
count: 1,
|
||||
value: {
|
||||
name: p.name,
|
||||
description: p.description,
|
||||
}
|
||||
};
|
||||
rows.push(row);
|
||||
}
|
||||
res.json({
|
||||
rows: rows
|
||||
});
|
||||
});
|
||||
var packages = result.searchMatchs.concat(result.keywordMatchs);
|
||||
|
||||
var rows = [];
|
||||
for (var i = 0; i < packages.length; i++) {
|
||||
var p = packages[i];
|
||||
var row = {
|
||||
key: p.name,
|
||||
count: 1,
|
||||
value: {
|
||||
name: p.name,
|
||||
description: p.description,
|
||||
}
|
||||
};
|
||||
rows.push(row);
|
||||
}
|
||||
this.body = {
|
||||
rows: rows
|
||||
};
|
||||
};
|
||||
|
||||
exports.displaySync = function (req, res, next) {
|
||||
var name = req.params.name || req.query.name;
|
||||
res.render('sync', {
|
||||
exports.displaySync = function *(next) {
|
||||
var name = this.params.name || this.query.name;
|
||||
yield this.render('sync', {
|
||||
name: name,
|
||||
title: 'Sync - ' + name
|
||||
});
|
||||
|
||||
@@ -15,31 +15,24 @@
|
||||
*/
|
||||
var Module = require('../../proxy/module');
|
||||
var User = require('../../proxy/user');
|
||||
var eventproxy = require('eventproxy');
|
||||
|
||||
exports.display = function (req, res, next) {
|
||||
var name = req.params.name;
|
||||
exports.display = function *(next) {
|
||||
var name = this.params.name;
|
||||
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(next);
|
||||
Module.listByAuthor(name, ep.done('packages'));
|
||||
User.get(name, ep.done('user'));
|
||||
var r = yield [Module.listByAuthor(name), User.get(name)];
|
||||
var packages = r[0];
|
||||
var user = r[1];
|
||||
if (!user && !packages.length) {
|
||||
return yield next;
|
||||
}
|
||||
user = {
|
||||
name: name,
|
||||
email: user && user.email
|
||||
};
|
||||
|
||||
ep.all('packages', 'user', function (packages, user) {
|
||||
//because of sync, maybe no this user in database,
|
||||
//but his packages in this registry
|
||||
if (!user && !packages.length) {
|
||||
return next();
|
||||
}
|
||||
user = {
|
||||
name: name,
|
||||
email: user && user.email
|
||||
};
|
||||
|
||||
return res.render('profile', {
|
||||
title: 'User - ' + name,
|
||||
packages: packages || [],
|
||||
user: user
|
||||
});
|
||||
yield this.render('profile', {
|
||||
title: 'User - ' + name,
|
||||
packages: packages || [],
|
||||
user: user
|
||||
});
|
||||
};
|
||||
|
||||
21
docs/db.sql
21
docs/db.sql
@@ -14,6 +14,17 @@ CREATE TABLE `user` (
|
||||
KEY `gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='user base info';
|
||||
|
||||
CREATE TABLE `module_keyword` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`keyword` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'keyword',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`description` longtext,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `keyword_module_name` (`keyword`,`name`),
|
||||
KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module keyword';
|
||||
|
||||
CREATE TABLE `module` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
@@ -101,3 +112,13 @@ CREATE TABLE `download_total` (
|
||||
UNIQUE KEY `date_name` (`date`, `name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module download total info';
|
||||
-- ALTER TABLE `download_total` CHANGE `name` `name` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
|
||||
|
||||
CREATE TABLE `module_deps` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`deps` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'which module depend on this module',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name_deps` (`name`,`deps`),
|
||||
KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module deps';
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## What is this?
|
||||
|
||||
> Private npm registry and web for Enterprise, base on MySQL and [Simple Store Service](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide).
|
||||
> 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).
|
||||
|
||||
@@ -139,7 +139,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]
|
||||
@@ -188,14 +188,15 @@ Release [History](/history).
|
||||
$ git summary
|
||||
|
||||
project : cnpmjs.org
|
||||
repo age : 5 weeks
|
||||
active : 98 days
|
||||
commits : 239
|
||||
files : 85
|
||||
repo age : 7 weeks
|
||||
active : 132 days
|
||||
commits : 315
|
||||
files : 88
|
||||
authors :
|
||||
140 fengmk2 58.6%
|
||||
98 dead_horse 41.0%
|
||||
1 Alsotang 0.4%
|
||||
190 fengmk2 60.3%
|
||||
122 dead_horse 38.7%
|
||||
2 4simple 0.6%
|
||||
1 Alsotang 0.3%
|
||||
```
|
||||
|
||||
## npm and cnpm relation
|
||||
|
||||
@@ -29,11 +29,11 @@ exports.getCDNKey = function (name, filename) {
|
||||
return '/' + name + '/-/' + filename;
|
||||
};
|
||||
|
||||
exports.setDownloadURL = function (pkg, req, host) {
|
||||
exports.setDownloadURL = function (pkg, ctx, host) {
|
||||
if (pkg.dist) {
|
||||
host = host || req.headers.host;
|
||||
host = host || ctx.get('host') || ctx.host;
|
||||
pkg.dist.tarball = util.format('%s://%s/%s/download/%s-%s.tgz',
|
||||
req.connection.encrypted ? 'https' : 'http',
|
||||
ctx.protocol,
|
||||
host, pkg.name, pkg.name, pkg.version);
|
||||
}
|
||||
};
|
||||
@@ -41,3 +41,17 @@ exports.setDownloadURL = function (pkg, req, host) {
|
||||
exports.isAdmin = function (username) {
|
||||
return typeof config.admins[username] === 'string';
|
||||
};
|
||||
|
||||
exports.isMaintainer = function (ctx, maintainers) {
|
||||
if (ctx.session.isAdmin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var username = ctx.session.name;
|
||||
maintainers = maintainers || [];
|
||||
var match = maintainers.filter(function (item) {
|
||||
return item.name === username;
|
||||
});
|
||||
|
||||
return match.length > 0;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - middleware/auth.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -20,41 +20,46 @@ var config = require('../config');
|
||||
var common = require('../lib/common');
|
||||
|
||||
module.exports = function (options) {
|
||||
return function auth(req, res, next) {
|
||||
req.session.onlySync = config.enablePrivate ? true : false;
|
||||
if (req.session.name) {
|
||||
req.session.isAdmin = common.isAdmin(req.session.name);
|
||||
debug('auth exists user: %s, onlySync: %s, isAdmin: %s, headers: %j',
|
||||
req.session.name, req.session.onlySync, req.session.isAdmin, req.headers);
|
||||
return next();
|
||||
return function *auth(next) {
|
||||
debug('%s, %s, %j', this.url, this.sessionId, this.session);
|
||||
if (!this.session) {
|
||||
// redis crash
|
||||
this.session = {};
|
||||
return yield next;
|
||||
}
|
||||
var authorization = (req.headers.authorization || '').split(' ')[1] || '';
|
||||
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;
|
||||
}
|
||||
var authorization = (this.get('authorization') || '').split(' ')[1] || '';
|
||||
authorization = authorization.trim();
|
||||
if (!authorization) {
|
||||
return next();
|
||||
return yield next;
|
||||
}
|
||||
|
||||
authorization = new Buffer(authorization, 'base64').toString().split(':');
|
||||
if (authorization.length !== 2) {
|
||||
return yield next;
|
||||
}
|
||||
|
||||
var username = authorization[0];
|
||||
var password = authorization[1];
|
||||
|
||||
User.auth(username, password, function (err, row) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
var row = yield User.auth(username, password);
|
||||
if (!row) {
|
||||
debug('auth fail user: %j, headers: %j', row, this.header);
|
||||
this.session.name = null;
|
||||
this.session.isAdmin = false;
|
||||
return yield next;
|
||||
}
|
||||
|
||||
if (!row) {
|
||||
debug('auth fail user: %j, headers: %j', row, req.headers);
|
||||
return res.json(401, {
|
||||
error: 'unauthorized',
|
||||
reason: 'Name or password is incorrect.'
|
||||
});
|
||||
}
|
||||
|
||||
req.session.name = row.name;
|
||||
req.session.isAdmin = common.isAdmin(req.session.name);
|
||||
debug('auth pass user: %j, onlySync: %s, isAdmin: %s, headers: %j',
|
||||
row, req.session.onlySync, req.session.isAdmin, req.headers);
|
||||
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;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -14,12 +14,14 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
module.exports = function login(req, res, next) {
|
||||
if (!req.session.name) {
|
||||
return res.json(401, {
|
||||
module.exports = function *login(next) {
|
||||
if (!this.session.name) {
|
||||
this.status = 401;
|
||||
this.body = {
|
||||
error: 'unauthorized',
|
||||
reason: 'Login first.'
|
||||
});
|
||||
};
|
||||
return;
|
||||
}
|
||||
next();
|
||||
yield next;
|
||||
};
|
||||
|
||||
@@ -24,8 +24,11 @@ var template = '<?xml version="1.0" encoding="UTF-8"?>\
|
||||
|
||||
var lastModifyDate = new Date();
|
||||
|
||||
module.exports = function publishable(req, res, next) {
|
||||
res.charset = res.charset || 'utf-8';
|
||||
res.setHeader('Content-Type', 'text/xml');
|
||||
res.send(template.replace('${host}', req.headers.host));
|
||||
module.exports = function *publishable(next) {
|
||||
if (this.path === '/opensearch.xml') {
|
||||
this.type = 'text/xml';
|
||||
this.charset = 'utf-8';
|
||||
this.body = template.replace('${host}', this.host);
|
||||
}
|
||||
yield next;
|
||||
};
|
||||
|
||||
@@ -14,13 +14,15 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
module.exports = function publishable(req, res, next) {
|
||||
if (req.session.onlySync && !req.session.isAdmin) {
|
||||
module.exports = function *publishable(next) {
|
||||
if (this.session.onlySync && !this.session.isAdmin) {
|
||||
// private mode, only admin user can publish
|
||||
return res.json(403, {
|
||||
this.status = 403;
|
||||
this.body = {
|
||||
error: 'no_perms',
|
||||
reason: 'Private mode enable, only admin can publish this module'
|
||||
});
|
||||
};
|
||||
return;
|
||||
}
|
||||
next();
|
||||
yield next;
|
||||
};
|
||||
|
||||
28
middleware/registry_not_found.js
Normal file
28
middleware/registry_not_found.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/registry_not_found.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
module.exports = function *notFound(next) {
|
||||
yield next;
|
||||
|
||||
if (this.status) {
|
||||
return;
|
||||
}
|
||||
this.status = 404;
|
||||
this.body = {
|
||||
error: 'not_found',
|
||||
reason: 'document not found'
|
||||
};
|
||||
};
|
||||
@@ -21,25 +21,25 @@ var config = require('../config');
|
||||
* req.session.allowSync - allow sync triggle by cnpm install
|
||||
*/
|
||||
|
||||
module.exports = function (req, res, next) {
|
||||
module.exports = function *(next) {
|
||||
if (!config.syncByInstall || !config.enablePrivate) {
|
||||
// only config.enablePrivate should enable sync on install
|
||||
return next();
|
||||
return yield next;
|
||||
}
|
||||
// request not by node, consider it request from web
|
||||
if (req.headers['user-agent'] && req.headers['user-agent'].indexOf('node') !== 0) {
|
||||
return next();
|
||||
if (this.get('user-agent') && this.get('user-agent').indexOf('node') !== 0) {
|
||||
return yield next;
|
||||
}
|
||||
|
||||
req.session.allowSync = true;
|
||||
if (req.session.isAdmin) {
|
||||
this.session.allowSync = true;
|
||||
if (this.session.isAdmin) {
|
||||
// if current user is admin, should not enable auto sync on install, because it would be unpublish
|
||||
req.session.allowSync = false;
|
||||
this.session.allowSync = false;
|
||||
}
|
||||
|
||||
// TODO: allow sync will let publish sync package...
|
||||
req.session.allowSync = false;
|
||||
this.session.allowSync = false;
|
||||
|
||||
debug('%s allowSync: %s', req.session.name, req.session.allowSync);
|
||||
next();
|
||||
debug('%s allowSync: %s', this.session.name, this.session.allowSync);
|
||||
yield next;
|
||||
};
|
||||
|
||||
27
middleware/web_not_found.js
Normal file
27
middleware/web_not_found.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/web_not_found.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
module.exports = function *notFound(next) {
|
||||
yield next;
|
||||
|
||||
if (this.status) {
|
||||
return;
|
||||
}
|
||||
this.status = 404;
|
||||
this.type = 'text/html';
|
||||
this.charset = 'utf-8';
|
||||
this.body = 'Cannot GET ' + this.path;
|
||||
};
|
||||
58
package.json
58
package.json
@@ -1,68 +1,58 @@
|
||||
{
|
||||
"name": "cnpmjs.org",
|
||||
"version": "0.2.14",
|
||||
"version": "0.3.4",
|
||||
"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 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)).)*$/",
|
||||
"data-cover-flags": {
|
||||
"debug": false
|
||||
}
|
||||
},
|
||||
"travis-cov": {
|
||||
"threshold": 90
|
||||
"cov": {
|
||||
"threshold": 83
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"bagpipe": "0.3.5",
|
||||
"connect": "2.12.0",
|
||||
"connect-markdown": "0.0.3",
|
||||
"connect-redis": "1.4.6",
|
||||
"connect-render": "0.3.2",
|
||||
"connect-rt": "0.0.2",
|
||||
"co-read": "0.0.1",
|
||||
"co-write": "0.3.0",
|
||||
"debug": "0.7.4",
|
||||
"eventproxy": "0.2.6",
|
||||
"eventproxy": "0.3.0",
|
||||
"forward": "0.0.4",
|
||||
"graceful": "0.0.5",
|
||||
"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.8",
|
||||
"logfilestream": "0.1.0",
|
||||
"marked": "0.3.0",
|
||||
"marked": "0.3.1",
|
||||
"microtime": "0.5.1",
|
||||
"mime": "1.2.11",
|
||||
"mkdirp": "0.3.5",
|
||||
"moment": "2.5.0",
|
||||
"moment": "2.5.1",
|
||||
"ms": "0.6.2",
|
||||
"mysql": "2.0.0",
|
||||
"nodemailer": "0.6.0",
|
||||
"mysql": "2.1.0",
|
||||
"nodemailer": "0.6.1",
|
||||
"qn": "0.2.0",
|
||||
"ready": "0.1.1",
|
||||
"response-cookie": "0.0.2",
|
||||
"response-patch": "0.1.1",
|
||||
"semver": "2.2.1",
|
||||
"thunkify-wrap": "0.0.3",
|
||||
"urllib": "0.5.5",
|
||||
"urlrouter": "0.5.4",
|
||||
"utility": "0.1.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autod": ">=0.0.10",
|
||||
"blanket": "*",
|
||||
"autod": ">=0.0.13",
|
||||
"contributors": "*",
|
||||
"coveralls": "*",
|
||||
"mm": "0.1.8",
|
||||
"cov": "*",
|
||||
"istanbul": "git://github.com/gotwarlost/istanbul.git#harmony",
|
||||
"mm": "0.2.0",
|
||||
"mocha": "*",
|
||||
"mocha-lcov-reporter": "*",
|
||||
"pedding": "0.0.3",
|
||||
"should": "2.1.1",
|
||||
"supertest": "0.8.3",
|
||||
"travis-cov": "*"
|
||||
"should": "3.1.3",
|
||||
"supertest": "0.9.0"
|
||||
},
|
||||
"homepage": "https://github.com/cnpm/cnpmjs.org",
|
||||
"repository": {
|
||||
@@ -78,7 +68,7 @@
|
||||
"cnpmjs.org", "npm", "npmjs", "npmjs.org", "registry"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
"node": ">= 0.11.9"
|
||||
},
|
||||
"author": [
|
||||
"fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var config = require('../config');
|
||||
var mysql = require('../common/mysql');
|
||||
|
||||
@@ -41,3 +42,5 @@ var SELECT_ALL_TOTAL_SQL = 'SELECT date, sum(count) AS count \
|
||||
exports.getTotal = function (start, end, callback) {
|
||||
mysql.query(SELECT_ALL_TOTAL_SQL, [start, end], callback);
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
162
proxy/module.js
162
proxy/module.js
@@ -14,6 +14,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var utility = require('utility');
|
||||
var eventproxy = require('eventproxy');
|
||||
var config = require('../config');
|
||||
@@ -29,6 +30,7 @@ var INSERT_MODULE_SQL = 'INSERT INTO module(gmt_create, gmt_modified, \
|
||||
dist_tarball=VALUES(dist_tarball), dist_shasum=VALUES(dist_shasum), dist_size=VALUES(dist_size);';
|
||||
|
||||
exports.add = function (mod, callback) {
|
||||
var keywords = mod.package.keywords;
|
||||
var pkg;
|
||||
try {
|
||||
pkg = stringifyPackage(mod.package);
|
||||
@@ -51,6 +53,74 @@ exports.add = function (mod, callback) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, {id: result.insertId, gmt_modified: new Date()});
|
||||
|
||||
if (typeof keywords === 'string') {
|
||||
keywords = [keywords];
|
||||
}
|
||||
|
||||
if (!Array.isArray(keywords)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var words = [];
|
||||
for (var i = 0; i < keywords.length; i++) {
|
||||
var w = keywords[i];
|
||||
if (typeof w === 'string') {
|
||||
w = w.trim();
|
||||
if (w) {
|
||||
words.push(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (words.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add keywords
|
||||
exports.addKeywords(mod, description, words, utility.noop);
|
||||
});
|
||||
};
|
||||
|
||||
var GET_KEYWORD_SQL = 'SELECT keyword FROM module_keyword WHERE name=? ORDER BY keyword;';
|
||||
|
||||
exports.getKeywords = function (name, callback) {
|
||||
mysql.query(GET_KEYWORD_SQL, [name], function (err, rows) {
|
||||
var keywords = [];
|
||||
if (rows && rows.length) {
|
||||
keywords = rows.map(function (r) {
|
||||
return r.keyword;
|
||||
});
|
||||
}
|
||||
callback(err, keywords);
|
||||
});
|
||||
};
|
||||
|
||||
var ADD_KEYWORD_SQL = 'INSERT INTO module_keyword(gmt_create, keyword, name, description) \
|
||||
VALUES(now(), ?, ?, ?) \
|
||||
ON DUPLICATE KEY UPDATE description=VALUES(description);';
|
||||
|
||||
exports.addKeywords = function (name, description, keywords, callback) {
|
||||
var sql = '';
|
||||
var values = [];
|
||||
for (var i = 0; i < keywords.length; i++) {
|
||||
sql += ADD_KEYWORD_SQL;
|
||||
values.push(keywords[i]);
|
||||
values.push(name);
|
||||
values.push(description);
|
||||
}
|
||||
mysql.query(sql, values, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var ids = [];
|
||||
for (var i = 0; i < results.length; i++) {
|
||||
var r = results[i];
|
||||
if (r.insertId) {
|
||||
ids.push(r.insertId);
|
||||
}
|
||||
}
|
||||
callback(null, ids);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -59,7 +129,21 @@ exports.updateDescription = function (id, description, callback) {
|
||||
mysql.query(UPDATE_DESC_SQL, [description, id], callback);
|
||||
};
|
||||
|
||||
var UPDATE_DIST_SQL = 'UPDATE module SET version=?, package=?, dist_tarball=?, dist_shasum=?, dist_size=? WHERE id=?;';
|
||||
var UPDATE_PACKAGE_SQL = 'UPDATE module SET package=? WHERE id=?;';
|
||||
exports.updateReadme = function (id, readme, callback) {
|
||||
exports.getById(id, function (err, data) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
data.package = data.package || {};
|
||||
data.package.readme = readme;
|
||||
var pkg = stringifyPackage(data.package);
|
||||
mysql.query(UPDATE_PACKAGE_SQL, [pkg, id], callback);
|
||||
});
|
||||
};
|
||||
|
||||
var UPDATE_DIST_SQL = 'UPDATE module SET publish_time=?, version=?, package=?, \
|
||||
dist_tarball=?, dist_shasum=?, dist_size=? WHERE id=?;';
|
||||
|
||||
exports.update = function (mod, callback) {
|
||||
var pkg;
|
||||
@@ -69,7 +153,8 @@ exports.update = function (mod, callback) {
|
||||
return callback(e);
|
||||
}
|
||||
var dist = mod.package.dist;
|
||||
mysql.query(UPDATE_DIST_SQL, [mod.version, pkg, dist.tarball, dist.shasum, dist.size, mod.id],
|
||||
mysql.query(UPDATE_DIST_SQL,
|
||||
[mod.publish_time, mod.version, pkg, dist.tarball, dist.shasum, dist.size, mod.id],
|
||||
function (err, result) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
@@ -91,6 +176,7 @@ function parseRow(row) {
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.parseRow = parseRow;
|
||||
|
||||
function stringifyPackage(pkg) {
|
||||
return encodeURIComponent(JSON.stringify(pkg));
|
||||
@@ -171,7 +257,12 @@ exports.removeTags = function (name, callback) {
|
||||
mysql.query(DELETE_TAGS_SQL, [name], callback);
|
||||
};
|
||||
|
||||
var SELECT_ALL_TAGS_SQL = 'SELECT tag, version, gmt_modified, module_id FROM tag WHERE name=?;';
|
||||
var DELETE_TAGS_BY_IDS_SQL = 'DELETE FROM tag WHERE id in (?)';
|
||||
exports.removeTagsByIds = function (ids, callback) {
|
||||
mysql.query(DELETE_TAGS_BY_IDS_SQL, [ids], callback);
|
||||
};
|
||||
|
||||
var SELECT_ALL_TAGS_SQL = 'SELECT id, tag, version, gmt_modified, module_id FROM tag WHERE name=?;';
|
||||
|
||||
exports.listTags = function (name, callback) {
|
||||
mysql.query(SELECT_ALL_TAGS_SQL, [name], callback);
|
||||
@@ -295,10 +386,10 @@ exports.listByAuthor = function (author, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var SEARCH_SQLS = [
|
||||
'SELECT module_id FROM tag WHERE name LIKE ? AND tag="latest" ORDER BY name LIMIT ?;',
|
||||
'SELECT name, description FROM module WHERE id IN (?) ORDER BY name;'
|
||||
];
|
||||
var SEARCH_MODULES_SQL = 'SELECT module_id FROM tag WHERE name LIKE ? AND tag="latest" ORDER BY name LIMIT ?;';
|
||||
var SEARCH_MODULES_BY_KEYWORD_SQL = 'SELECT name, description FROM module_keyword WHERE keyword = ? ORDER BY id DESC LIMIT ?;';
|
||||
var QUERY_MODULES_BY_ID_SQL = 'SELECT name, description FROM module WHERE id IN (?) ORDER BY name;';
|
||||
|
||||
exports.search = function (word, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
@@ -306,21 +397,58 @@ exports.search = function (word, options, callback) {
|
||||
}
|
||||
options = options || {};
|
||||
var limit = options.limit || 100;
|
||||
word = word.replace(/^%/, '') + '%'; //ignore prefix %
|
||||
word = word.replace(/^%/, ''); //ignore prefix %
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(callback);
|
||||
mysql.query(SEARCH_SQLS[0], [word, limit], ep.done(function (rows) {
|
||||
if (!rows || rows.length === 0) {
|
||||
return callback(null, []);
|
||||
|
||||
// search flows:
|
||||
// 1. prefix search by name
|
||||
// 2. like search by name
|
||||
// 3. keyword equal search
|
||||
var ids = {};
|
||||
|
||||
mysql.query(SEARCH_MODULES_SQL, [word + '%', limit ], ep.done(function (rows) {
|
||||
rows = rows || [];
|
||||
if (rows.length > 0) {
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
ids[rows[i].module_id] = 1;
|
||||
}
|
||||
}
|
||||
ep.emit('ids', rows.map(function (r) {
|
||||
return r.module_id;
|
||||
}));
|
||||
if (rows.length >= 20) {
|
||||
return ep.emit('ids', Object.keys(ids));
|
||||
}
|
||||
|
||||
mysql.query(SEARCH_MODULES_SQL, [ '%' + word + '%', limit ], ep.done('likeSearch'));
|
||||
}));
|
||||
|
||||
ep.on('ids', function (ids) {
|
||||
mysql.query(SEARCH_SQLS[1], [ids], ep.done(function (modules) {
|
||||
callback(null, modules);
|
||||
mysql.query(SEARCH_MODULES_BY_KEYWORD_SQL, [ word, limit ], ep.done('keywordRows'));
|
||||
|
||||
ep.on('likeSearch', function (rows) {
|
||||
rows = rows || [];
|
||||
if (rows.length > 0) {
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
ids[rows[i].module_id] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
ep.emit('ids', Object.keys(ids));
|
||||
});
|
||||
|
||||
ep.all('ids', 'keywordRows', function (ids, keywordRows) {
|
||||
keywordRows = keywordRows || [];
|
||||
var data = {
|
||||
keywordMatchs: keywordRows,
|
||||
searchMatchs: []
|
||||
};
|
||||
if (ids.length === 0) {
|
||||
return callback(null, data);
|
||||
}
|
||||
|
||||
mysql.query(QUERY_MODULES_BY_ID_SQL, [ids], ep.done(function (modules) {
|
||||
data.searchMatchs = modules;
|
||||
callback(null, data);
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
44
proxy/module_deps.js
Normal file
44
proxy/module_deps.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/**!
|
||||
* cnpmjs.org - proxy/module_deps.js
|
||||
*
|
||||
* Copyright(c) 2014
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var mysql = require('../common/mysql');
|
||||
|
||||
var LIST_DEPS_SQL = 'SELECT deps FROM module_deps WHERE name=?;';
|
||||
|
||||
exports.list = function (name, callback) {
|
||||
mysql.query(LIST_DEPS_SQL, [name], callback);
|
||||
};
|
||||
|
||||
var INSERT_DEPS_SQL = 'INSERT INTO module_deps(gmt_create, name, deps) \
|
||||
VALUES(now(), ?, ?);';
|
||||
|
||||
exports.add = function (name, deps, callback) {
|
||||
mysql.query(INSERT_DEPS_SQL, [name, deps], function (err, result) {
|
||||
if (err && err.code === 'ER_DUP_ENTRY') {
|
||||
err = null;
|
||||
}
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
var DELETE_DEPS_SQL = 'DELETE FROM module_deps WHERE name=? AND deps=?;';
|
||||
|
||||
exports.remove = function (name, deps, callback) {
|
||||
mysql.query(DELETE_DEPS_SQL, [name, deps], callback);
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
@@ -14,6 +14,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var mysql = require('../common/mysql');
|
||||
|
||||
var INSERT_LOG_SQL = 'INSERT INTO module_log(gmt_create, gmt_modified, name, username, log) \
|
||||
@@ -40,3 +41,5 @@ var SELECT_SQL = 'SELECT * FROM module_log WHERE id=?;';
|
||||
exports.get = function (id, callback) {
|
||||
mysql.queryOne(SELECT_SQL, [id], callback);
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
14
proxy/npm.js
14
proxy/npm.js
@@ -14,6 +14,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var urllib = require('urllib');
|
||||
var config = require('../config');
|
||||
|
||||
@@ -26,7 +27,16 @@ function request(url, options, callback) {
|
||||
options.dataType = options.dataType || 'json';
|
||||
options.timeout = options.timeout || 120000;
|
||||
url = config.sourceNpmRegistry + url;
|
||||
urllib.request(url, options, callback);
|
||||
urllib.request(url, options, function (err, data, res) {
|
||||
if (err) {
|
||||
var statusCode = res && res.statusCode || -1;
|
||||
if (err.name === 'JSONResponseFormatError' && statusCode >= 500) {
|
||||
err.name = 'NPMServerError';
|
||||
err.message = 'Status ' + statusCode + ', ' + (data && data.toString() || 'empty body');
|
||||
}
|
||||
}
|
||||
callback(err, data, res);
|
||||
});
|
||||
}
|
||||
|
||||
exports.get = function (name, callback) {
|
||||
@@ -55,3 +65,5 @@ exports.getShort = function (callback) {
|
||||
timeout: 300000
|
||||
}, callback);
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var debug = require('debug')('cnpmjs.org:proxy:sync_module_worker');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var util = require('util');
|
||||
@@ -24,12 +25,14 @@ var crypto = require('crypto');
|
||||
var eventproxy = require('eventproxy');
|
||||
var urllib = require('urllib');
|
||||
var utility = require('utility');
|
||||
var ms = require('ms');
|
||||
var nfs = require('../common/nfs');
|
||||
var npm = require('./npm');
|
||||
var common = require('../lib/common');
|
||||
var Module = require('./module');
|
||||
var ModuleDeps = require('./module_deps');
|
||||
var Log = require('./module_log');
|
||||
var ms = require('ms');
|
||||
var config = require('../config');
|
||||
|
||||
function SyncModuleWorker(options) {
|
||||
EventEmitter.call(this);
|
||||
@@ -194,6 +197,8 @@ SyncModuleWorker.prototype._sync = function (name, pkg, callback) {
|
||||
var missingVersions = [];
|
||||
var missingTags = [];
|
||||
var missingDescriptions = [];
|
||||
var missingReadmes = [];
|
||||
|
||||
ep.all('existsMap', 'existsTags', function (map, tags) {
|
||||
var times = pkg.time || {};
|
||||
pkg.versions = pkg.versions || {};
|
||||
@@ -239,7 +244,10 @@ SyncModuleWorker.prototype._sync = function (name, pkg, callback) {
|
||||
if (!version || !version.dist) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//patch for readme
|
||||
if (!version.readme) {
|
||||
version.readme = pkg.readme;
|
||||
}
|
||||
var publish_time = times[v];
|
||||
version.publish_time = publish_time ? Date.parse(publish_time) : null;
|
||||
if (!version.maintainers || !version.maintainers[0]) {
|
||||
@@ -261,6 +269,14 @@ SyncModuleWorker.prototype._sync = function (name, pkg, callback) {
|
||||
description: version.description
|
||||
});
|
||||
}
|
||||
|
||||
if (!exists.package.readme && version.readme) {
|
||||
// * make sure readme exists
|
||||
missingReadmes.push({
|
||||
id: exists.id,
|
||||
readme: version.readme
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -317,10 +333,11 @@ SyncModuleWorker.prototype._sync = function (name, pkg, callback) {
|
||||
missingDescriptions.forEach(function (item) {
|
||||
Module.updateDescription(item.id, item.description, function (err, result) {
|
||||
if (err) {
|
||||
return that.log(' save error, %s: description %j, error: %s', item.id, item.description, err);
|
||||
that.log(' save error, id: %s, description: %s, error: %s', item.id, item.description, err);
|
||||
} else {
|
||||
that.log(' saved, id: %s, description length: %d', item.id, item.description.length);
|
||||
}
|
||||
that.log(' saved, id: %s, description length: %d', item.id, item.description.length);
|
||||
ep.emit('saveDescription');
|
||||
ep.emitLater('saveDescription');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -339,7 +356,7 @@ SyncModuleWorker.prototype._sync = function (name, pkg, callback) {
|
||||
missingTags.forEach(function (item) {
|
||||
Module.addTag(name, item[0], item[1], ep.done(function (result) {
|
||||
that.log(' added tag %s:%s, module_id: %s', item[0], item[1], result && result.module_id);
|
||||
ep.emit('addTag');
|
||||
ep.emitLater('addTag');
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -348,7 +365,29 @@ SyncModuleWorker.prototype._sync = function (name, pkg, callback) {
|
||||
});
|
||||
});
|
||||
|
||||
ep.all('tagDone', 'descriptionDone', function () {
|
||||
ep.once('syncDone', function () {
|
||||
if (missingReadmes.length === 0) {
|
||||
return ep.emit('readmeDone');
|
||||
}
|
||||
|
||||
that.log(' [%s] saving %d readmes', name, missingReadmes.length);
|
||||
missingReadmes.forEach(function (item) {
|
||||
Module.updateReadme(item.id, item.readme, function (err, result) {
|
||||
if (err) {
|
||||
that.log(' save error, id: %s, error: %s', item.id, err);
|
||||
} else {
|
||||
that.log(' saved, id: %s', item.id);
|
||||
}
|
||||
ep.emitLater('saveReadme');
|
||||
});
|
||||
});
|
||||
|
||||
ep.after('saveReadme', missingReadmes.length, function () {
|
||||
ep.emit('readmeDone');
|
||||
});
|
||||
});
|
||||
|
||||
ep.all('tagDone', 'descriptionDone', 'readmeDone', function () {
|
||||
// TODO: set latest version
|
||||
callback(null, versionNames);
|
||||
});
|
||||
@@ -373,19 +412,37 @@ SyncModuleWorker.prototype._syncOneVersion = function (versionIndex, sourcePacka
|
||||
callback(err);
|
||||
});
|
||||
|
||||
that.log(' [%s:%d] syncing, version: %s, dist: %j, no deps: %s, publish on cnpm: %s',
|
||||
var dependencies = Object.keys(sourcePackage.dependencies || {});
|
||||
var devDependencies = Object.keys(sourcePackage.devDependencies || {});
|
||||
|
||||
that.log(' [%s:%d] syncing, version: %s, dist: %j, no deps: %s, publish on cnpm: %s, dependencies: %d, devDependencies: %d',
|
||||
sourcePackage.name, versionIndex, sourcePackage.version,
|
||||
sourcePackage.dist, that.noDep, that._publish);
|
||||
sourcePackage.dist, that.noDep, that._publish,
|
||||
dependencies.length, devDependencies.length);
|
||||
|
||||
if (dependencies.length > config.maxDependencies) {
|
||||
dependencies = dependencies.slice(0, config.maxDependencies);
|
||||
}
|
||||
|
||||
if (devDependencies.length > config.maxDependencies) {
|
||||
devDependencies = devDependencies.slice(0, config.maxDependencies);
|
||||
}
|
||||
|
||||
if (!that.noDep) {
|
||||
for (var k in sourcePackage.dependencies) {
|
||||
that.add(k);
|
||||
for (var i = 0; i < dependencies.length; i++) {
|
||||
that.add(dependencies[i]);
|
||||
}
|
||||
|
||||
for (var k in sourcePackage.devDependencies) {
|
||||
that.add(k);
|
||||
for (var i = 0; i < devDependencies.length; i++) {
|
||||
that.add(devDependencies[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// add deps relations
|
||||
dependencies.forEach(function (depName) {
|
||||
ModuleDeps.add(depName, sourcePackage.name, utility.noop);
|
||||
});
|
||||
|
||||
var shasum = crypto.createHash('sha1');
|
||||
var dataSize = 0;
|
||||
urllib.request(downurl, options, ep.done(function (_, response) {
|
||||
@@ -518,3 +575,5 @@ SyncModuleWorker.sync = function (name, username, options, callback) {
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
SyncModuleWorker.sync = thunkify(SyncModuleWorker.sync);
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var eventproxy = require('eventproxy');
|
||||
var config = require('../config');
|
||||
var mysql = require('../common/mysql');
|
||||
var eventproxy = require('eventproxy');
|
||||
|
||||
var DB_SIZE_SQL = 'SELECT TABLE_NAME AS name, data_length, index_length \
|
||||
FROM information_schema.tables \
|
||||
@@ -118,3 +119,5 @@ exports.updateSyncNum = function (params, callback) {
|
||||
];
|
||||
mysql.query(UPDATE_SYNC_NUM_SQL, query, callback);
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var utility = require('utility');
|
||||
var config = require('../config');
|
||||
var mysql = require('../common/mysql');
|
||||
@@ -29,7 +30,6 @@ function sha1(s) {
|
||||
function passwordSha(password, salt) {
|
||||
return sha1(password + salt);
|
||||
}
|
||||
exports.passwordSha = passwordSha;
|
||||
|
||||
exports.get = function (name, callback) {
|
||||
mysql.queryOne(SELECT_USER_SQL, [name], function (err, row) {
|
||||
@@ -105,3 +105,5 @@ exports.update = function (user, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
exports.passwordSha = passwordSha;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var middlewares = require('koa-middlewares');
|
||||
var login = require('../middleware/login');
|
||||
var publishable = require('../middleware/publishable');
|
||||
var syncByInstall = require('../middleware/sync_by_install');
|
||||
@@ -24,7 +25,7 @@ var user = require('../controllers/registry/user');
|
||||
var sync = require('../controllers/sync');
|
||||
|
||||
function routes(app) {
|
||||
app.get('/', total.show);
|
||||
app.get('/', middlewares.jsonp(), total.show);
|
||||
|
||||
//before /:name/:version
|
||||
//get all modules, for npm search
|
||||
@@ -34,10 +35,10 @@ function routes(app) {
|
||||
app.get('/-/short', mod.listAllModuleNames);
|
||||
|
||||
// module
|
||||
app.get('/:name', [syncByInstall], mod.show);
|
||||
app.get('/:name/:version', [syncByInstall], mod.get);
|
||||
app.get('/:name', syncByInstall, mod.show);
|
||||
app.get('/:name/:version', syncByInstall, mod.get);
|
||||
// try to add module
|
||||
app.put('/:name', [login, publishable], mod.add);
|
||||
app.put('/:name', login, publishable, mod.add);
|
||||
|
||||
// sync from source npm
|
||||
app.put('/:name/sync', sync.sync);
|
||||
@@ -47,23 +48,22 @@ function routes(app) {
|
||||
|
||||
// put tarball
|
||||
// https://registry.npmjs.org/cnpmjs.org/-/cnpmjs.org-0.0.0.tgz/-rev/1-c85bc65e8d2470cc4d82b8f40da65b8e
|
||||
app.put('/:name/-/:filename/-rev/:rev', [login, publishable], mod.upload);
|
||||
app.put('/:name/-/:filename/-rev/:rev', login, publishable, mod.upload);
|
||||
// delete tarball
|
||||
app.delete('/:name/download/:filename/-rev/:rev', [login, publishable], mod.removeTar);
|
||||
app.delete('/:name/download/:filename/-rev/:rev', login, publishable, mod.removeTar);
|
||||
|
||||
// put package.json to module
|
||||
app.put('/:name/:version/-tag/latest', [login, publishable], mod.updateLatest);
|
||||
app.put('/:name/:version/-tag/latest', login, publishable, mod.updateLatest);
|
||||
|
||||
// update module, unpublish will PUT this
|
||||
app.put('/:name/-rev/:rev', [login, publishable], mod.removeWithVersions);
|
||||
app.delete('/:name/-rev/:rev', [login, publishable], mod.removeAll);
|
||||
app.put('/:name/-rev/:rev', login, publishable, mod.removeWithVersions);
|
||||
app.delete('/:name/-rev/:rev', login, publishable, mod.removeAll);
|
||||
|
||||
// try to create a new user
|
||||
// https://registry.npmjs.org/-/user/org.couchdb.user:fengmk2
|
||||
app.put('/-/user/org.couchdb.user::name', user.add);
|
||||
app.get('/-/user/org.couchdb.user::name', user.show);
|
||||
app.put('/-/user/org.couchdb.user::name/-rev/:rev', login, user.update);
|
||||
|
||||
// _session
|
||||
app.post('/_session', user.authSession);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - servers/registry.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -15,69 +15,55 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
require('response-patch');
|
||||
var koa = require('koa');
|
||||
var app = module.exports = koa();
|
||||
var http = require('http');
|
||||
var connect = require('connect');
|
||||
var rt = require('connect-rt');
|
||||
var responseCookie = require('response-cookie');
|
||||
var urlrouter = require('urlrouter');
|
||||
var forward = require('forward');
|
||||
var path = require('path');
|
||||
var middlewares = require('koa-middlewares');
|
||||
var routes = require('../routes/registry');
|
||||
var logger = require('../common/logger');
|
||||
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);
|
||||
var app = connect();
|
||||
|
||||
app.use(rt({headerName: 'X-ReadTime'}));
|
||||
app.use(function (req, res, next) {
|
||||
res.req = req;
|
||||
next();
|
||||
});
|
||||
app.use(middlewares.rt({headerName: 'X-ReadTime'}));
|
||||
|
||||
app.use('/favicon.ico', forward(path.join(rootdir, 'public', 'favicon.png')));
|
||||
app.use(middlewares.rewrite('/favicon.ico', '/public/favicon.ico'));
|
||||
|
||||
app.use('/dist', connect.static(config.uploadDir));
|
||||
|
||||
app.use(responseCookie());
|
||||
app.use(connect.cookieParser());
|
||||
app.use(connect.query());
|
||||
app.use(connect.json());
|
||||
app.keys = ['todokey', config.sessionSecret];
|
||||
app.outputErrors = true;
|
||||
app.use(session);
|
||||
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());
|
||||
|
||||
/**
|
||||
* Routes
|
||||
*/
|
||||
|
||||
app.use(urlrouter(routes));
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
res.json(404, {error: 'not_found', reason: 'document not found'});
|
||||
});
|
||||
app.use(middlewares.router(app));
|
||||
routes(app);
|
||||
|
||||
/**
|
||||
* Error handler
|
||||
*/
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
err.url = err.url || req.url;
|
||||
app.on('error', function (err, ctx) {
|
||||
err.url = err.url || ctx.request.url;
|
||||
logger.error(err);
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
console.error(err.stack);
|
||||
}
|
||||
if (config.debug) {
|
||||
return next(err);
|
||||
}
|
||||
res.json(500, {
|
||||
error: err.name,
|
||||
reason: err.message
|
||||
});
|
||||
});
|
||||
|
||||
app = http.createServer(app);
|
||||
app = http.createServer(app.callback());
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(config.registryPort);
|
||||
}
|
||||
|
||||
module.exports = app;
|
||||
|
||||
@@ -15,47 +15,52 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
require('response-patch');
|
||||
var path = require('path');
|
||||
var http = require('http');
|
||||
var fs = require('fs');
|
||||
var connect = require('connect');
|
||||
var rt = require('connect-rt');
|
||||
var urlrouter = require('urlrouter');
|
||||
var connectMarkdown = require('connect-markdown');
|
||||
var koa = require('koa');
|
||||
var middlewares = require('koa-middlewares');
|
||||
var markdown = require('koa-markdown');
|
||||
var session = require('../common/session');
|
||||
var opensearch = require('../middleware/opensearch');
|
||||
var notFound = require('../middleware/web_not_found');
|
||||
var routes = require('../routes/web');
|
||||
var logger = require('../common/logger');
|
||||
var config = require('../config');
|
||||
var session = require('../common/session');
|
||||
var render = require('connect-render');
|
||||
var opensearch = require('../middleware/opensearch');
|
||||
var app = connect();
|
||||
|
||||
var app = koa();
|
||||
|
||||
var rootdir = path.dirname(__dirname);
|
||||
|
||||
app.use(rt({headerName: 'X-ReadTime'}));
|
||||
app.use(function (req, res, next) {
|
||||
res.req = req;
|
||||
next();
|
||||
});
|
||||
|
||||
app.use('/public', connect.static(path.join(rootdir, 'public')));
|
||||
app.use('/opensearch.xml', opensearch);
|
||||
|
||||
app.use(connect.cookieParser());
|
||||
app.use(middlewares.rt({headerName: 'X-ReadTime'}));
|
||||
app.use(middlewares.staticCache(path.join(__dirname, '..', 'public'), {
|
||||
buffer: !config.debug,
|
||||
maxAge: config.debug ? 0 : 60 * 60 * 24 * 7,
|
||||
dir: path.join(rootdir, 'public')
|
||||
}));
|
||||
app.use(opensearch);
|
||||
app.keys = ['todokey', config.sessionSecret];
|
||||
app.outputErrors = true;
|
||||
app.use(session);
|
||||
app.use(connect.query());
|
||||
app.use(connect.json());
|
||||
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');
|
||||
|
||||
var layoutFile = path.join(viewDir, '_layout.html');
|
||||
var footer = config.customFooter || fs.readFileSync(path.join(viewDir, 'footer.html'), 'utf8');
|
||||
var layout = fs.readFileSync(path.join(viewDir, 'layout.html'), 'utf8').replace('{{footer}}', footer);
|
||||
var layout = fs.readFileSync(path.join(viewDir, 'layout.html'), 'utf8')
|
||||
.replace('{{footer}}', footer)
|
||||
.replace('{{logoURL}}', config.logoURL);
|
||||
fs.writeFileSync(layoutFile, layout);
|
||||
|
||||
app.use('/', connectMarkdown({
|
||||
app.use(markdown({
|
||||
baseUrl: '/',
|
||||
root: docDir,
|
||||
layout: layoutFile,
|
||||
titleHolder: '<%- locals.title %>',
|
||||
@@ -63,41 +68,38 @@ app.use('/', connectMarkdown({
|
||||
indexName: 'readme'
|
||||
}));
|
||||
|
||||
var helpers = {
|
||||
var locals = {
|
||||
config: config
|
||||
};
|
||||
|
||||
app.use(render({
|
||||
middlewares.render(app, {
|
||||
root: viewDir,
|
||||
viewExt: '.html',
|
||||
layout: 'layout',
|
||||
viewExt: 'html',
|
||||
layout: '_layout',
|
||||
cache: config.viewCache,
|
||||
helpers: helpers
|
||||
}));
|
||||
debug: config.debug,
|
||||
locals: locals
|
||||
});
|
||||
|
||||
/**
|
||||
* Routes
|
||||
*/
|
||||
|
||||
app.use(urlrouter(routes));
|
||||
app.use(middlewares.router(app));
|
||||
routes(app);
|
||||
|
||||
/**
|
||||
* Error handler
|
||||
*/
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
err.url = err.url || req.url;
|
||||
app.on('error', function (err, ctx) {
|
||||
err.url = err.url || ctx.request.url;
|
||||
logger.error(err);
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
console.error(err.stack);
|
||||
}
|
||||
if (config.debug) {
|
||||
return next(err);
|
||||
}
|
||||
res.statusCode = 500;
|
||||
res.send('Server has some problems. :(');
|
||||
});
|
||||
|
||||
app = http.createServer(app);
|
||||
app = http.createServer(app.callback());
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(config.webPort);
|
||||
}
|
||||
|
||||
module.exports = app;
|
||||
|
||||
@@ -106,7 +106,7 @@ function getMissPackages(callback) {
|
||||
}
|
||||
|
||||
//only sync not exist once
|
||||
var syncNotExist = true;
|
||||
var syncNotExist = false;
|
||||
module.exports = function sync(callback) {
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(callback);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var should = require('should');
|
||||
var request = require('supertest');
|
||||
var mm = require('mm');
|
||||
@@ -25,6 +26,7 @@ var app = require('../../../servers/registry');
|
||||
var Module = require('../../../proxy/module');
|
||||
var Npm = require('../../../proxy/npm');
|
||||
var controller = require('../../../controllers/registry/module');
|
||||
var ModuleDeps = require('../../../proxy/module_deps');
|
||||
|
||||
var fixtures = path.join(path.dirname(path.dirname(__dirname)), 'fixtures');
|
||||
|
||||
@@ -64,25 +66,52 @@ describe('controllers/registry/module.test.js', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /:name', function () {
|
||||
it('should return module info', function (done) {
|
||||
describe('GET /:name get module package info', function () {
|
||||
var etag;
|
||||
|
||||
it('should return module info and etag', function (done) {
|
||||
request(app)
|
||||
.get('/cnpmjs.org')
|
||||
.expect(200, function (err, res) {
|
||||
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');
|
||||
// res.body.author.should.eql({
|
||||
// "name": "fengmk2",
|
||||
// "email": "fengmk2@gmail.com",
|
||||
// "url": "http://fengmk2.github.com"
|
||||
// });
|
||||
res.body.name.should.equal('cnpmjs.org');
|
||||
res.body.versions[Object.keys(res.body.versions)[0]].dist.tarball.should.include('/cnpmjs.org/download');
|
||||
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');
|
||||
res.body.name.should.equal('cnpmjs.org');
|
||||
res.body.versions[Object.keys(res.body.versions)[0]].dist.tarball.should.include('/cnpmjs.org/download');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should 304 when etag match', function (done) {
|
||||
request(app)
|
||||
.get('/cnpmjs.org')
|
||||
.set('If-None-Match', etag)
|
||||
.expect(304, done)
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /:name/:(version|tag)', function () {
|
||||
@@ -96,6 +125,9 @@ describe('controllers/registry/module.test.js', function () {
|
||||
body.version.should.equal('0.2.1');
|
||||
body._id.should.equal('cnpmjs.org@0.2.1');
|
||||
body.dist.tarball.should.include('cnpmjs.org-0.2.1.tgz');
|
||||
body.should.have.property('_cnpm_publish_time');
|
||||
body._cnpm_publish_time.should.be.a.Number;
|
||||
body.should.have.property('_publish_on_cnpm', true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -137,6 +169,13 @@ describe('controllers/registry/module.test.js', function () {
|
||||
name: 'cnpmjstest10',
|
||||
email: 'cnpmjstest10@cnpmjs.org'
|
||||
}],
|
||||
keywords: [
|
||||
'testputmodule', 'test', 'cnpmjstest'
|
||||
],
|
||||
dependencies: {
|
||||
'foo-testputmodule': "*",
|
||||
'bar-testputmodule': '*'
|
||||
}
|
||||
};
|
||||
var baseauth = 'Basic ' + new Buffer('cnpmjstest10:cnpmjstest10').toString('base64');
|
||||
var baseauthOther = 'Basic ' + new Buffer('cnpmjstest101:cnpmjstest101').toString('base64');
|
||||
@@ -284,6 +323,7 @@ describe('controllers/registry/module.test.js', function () {
|
||||
var pkg = require(path.join(fixtures, 'testputmodule.json')).versions['0.1.8'];
|
||||
pkg.name = 'testputmodule';
|
||||
pkg.version = '0.1.9';
|
||||
pkg.dependencies['foo-testputmodule'] = '*';
|
||||
request(app)
|
||||
.put('/' + pkg.name + '/' + pkg.version + '/-tag/latest')
|
||||
.set('authorization', baseauth)
|
||||
@@ -291,7 +331,16 @@ describe('controllers/registry/module.test.js', function () {
|
||||
.expect(201, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'rev');
|
||||
done();
|
||||
// should get deps foo-testputmodule contains 'testputmodule'
|
||||
ModuleDeps.list('foo-testputmodule', function (err, rows) {
|
||||
should.not.exist(err);
|
||||
var exists = rows.filter(function (r) {
|
||||
return r.deps === 'testputmodule';
|
||||
});
|
||||
exists.should.length(1);
|
||||
exists[0].deps.should.equal('testputmodule');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -339,6 +388,60 @@ describe('controllers/registry/module.test.js', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should publish with tgz base64, addPackageAndDist()', function (done) {
|
||||
var pkg = require(path.join(fixtures, 'package_and_tgz.json'));
|
||||
// delete first
|
||||
request(app)
|
||||
.del('/' + pkg.name + '/-rev/1')
|
||||
.set('authorization', baseauth)
|
||||
.expect({ok: true})
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', baseauth)
|
||||
.send(pkg)
|
||||
.expect(201, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'rev');
|
||||
res.body.ok.should.equal(true);
|
||||
|
||||
// upload again should 409
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', baseauth)
|
||||
.send(pkg)
|
||||
.expect(409, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.eql({
|
||||
error: 'conflict',
|
||||
reason: 'Document update conflict.'
|
||||
});
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should version_error when versions missing', function (done) {
|
||||
var pkg = require(path.join(fixtures, 'package_and_tgz.json'));
|
||||
delete pkg.versions;
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', baseauth)
|
||||
.send(pkg)
|
||||
.expect(400, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.eql({
|
||||
error: 'version_error',
|
||||
reason: 'filename or version not found, filename: mk2testmodule-0.0.1.tgz, version: undefined'
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /-/all', function () {
|
||||
@@ -435,6 +538,7 @@ describe('controllers/registry/module.test.js', function () {
|
||||
it('should remove version ok', function (done) {
|
||||
//do not really remove it here
|
||||
mm.empty(Module, 'removeByNameAndVersions');
|
||||
mm.empty(Module, 'removeTagsByIds');
|
||||
request(app)
|
||||
.put('/testputmodule/-rev/' + lastRev)
|
||||
.set('authorization', baseauth)
|
||||
@@ -453,34 +557,6 @@ describe('controllers/registry/module.test.js', function () {
|
||||
.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', 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', 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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE /:name/download/:filename/-rev/:rev', function () {
|
||||
|
||||
@@ -16,18 +16,22 @@
|
||||
|
||||
var should = require('should');
|
||||
var request = require('supertest');
|
||||
var mm = require('mm');
|
||||
var app = require('../../../servers/registry');
|
||||
var user = require('../../../proxy/user');
|
||||
var mm = require('mm');
|
||||
var mysql = require('../../../common/mysql');
|
||||
|
||||
describe('controllers/registry/user.test.js', function () {
|
||||
before(function (done) {
|
||||
app.listen(0, done);
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
app.close(done);
|
||||
});
|
||||
|
||||
afterEach(mm.restore);
|
||||
|
||||
describe('GET /-/user/org.couchdb.user:name', function () {
|
||||
it('should return user info', function (done) {
|
||||
request(app)
|
||||
@@ -47,7 +51,7 @@ describe('controllers/registry/user.test.js', function () {
|
||||
});
|
||||
|
||||
it('should return 500 when mysql error', function (done) {
|
||||
mm.error(user, 'get', 'mock error');
|
||||
mm.error(mysql, 'query', 'mock mysql error');
|
||||
request(app)
|
||||
.get('/-/user/org.couchdb.user:cnpmjstest1')
|
||||
.expect(500, done);
|
||||
@@ -91,7 +95,7 @@ describe('controllers/registry/user.test.js', function () {
|
||||
password_sha: 'password_sha',
|
||||
email: 'email'
|
||||
})
|
||||
.expect(500, done);
|
||||
.expect(500, done);
|
||||
});
|
||||
|
||||
it('should 201 when user.add ok', function (done) {
|
||||
|
||||
@@ -58,20 +58,21 @@ describe('controllers/sync.test.js', function () {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'logId');
|
||||
logIdRegistry = res.body.logId;
|
||||
setTimeout(function () {
|
||||
request(registryApp)
|
||||
.get('/utility')
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
should.not.exist(err);
|
||||
Object.keys(res.body.versions).length.should.above(0);
|
||||
for (var v in res.body.versions) {
|
||||
var pkg = res.body.versions[v];
|
||||
pkg.should.have.property('_publish_on_cnpm', true);
|
||||
}
|
||||
done();
|
||||
});
|
||||
}, 3000);
|
||||
done();
|
||||
// setTimeout(function () {
|
||||
// request(registryApp)
|
||||
// .get('/utility')
|
||||
// .expect(200)
|
||||
// .end(function (err, res) {
|
||||
// should.not.exist(err);
|
||||
// Object.keys(res.body.versions).length.should.above(0);
|
||||
// for (var v in res.body.versions) {
|
||||
// var pkg = res.body.versions[v];
|
||||
// pkg.should.have.property('_publish_on_cnpm', true);
|
||||
// }
|
||||
// done();
|
||||
// });
|
||||
// }, 5000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - test/controllers/total.test.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
var should = require('should');
|
||||
var request = require('supertest');
|
||||
var pedding = require('pedding');
|
||||
var registryApp = require('../../servers/registry');
|
||||
var webApp = require('../../servers/web');
|
||||
var pedding = require('pedding');
|
||||
|
||||
describe('controllers/total.test.js', function () {
|
||||
before(function (done) {
|
||||
@@ -44,6 +44,7 @@ describe('controllers/total.test.js', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return total info by jsonp', function (done) {
|
||||
request(registryApp)
|
||||
.get('?callback=totalCallback')
|
||||
@@ -51,7 +52,8 @@ describe('controllers/total.test.js', function () {
|
||||
.expect(/totalCallback\({.*}\)/, done);
|
||||
});
|
||||
});
|
||||
describe('GET /total in web', function () {
|
||||
|
||||
describe.skip('GET /total in web', function () {
|
||||
it('should return total info', function (done) {
|
||||
request(webApp)
|
||||
.get('/total')
|
||||
@@ -62,5 +64,5 @@ describe('controllers/total.test.js', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,11 +38,11 @@ describe('controllers/web/package.test.js', function () {
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('rows');
|
||||
res.body.rows.should.eql([
|
||||
{ key: 'c', count: 1, value: { name: 'c', description: 'Give folders or directories comments and view them easy.' } },
|
||||
{ key: 'charset', count: 1,
|
||||
value: { name: 'charset', description: 'Get the content charset from header and html content-type.' } }
|
||||
]);
|
||||
res.body.rows.length.should.above(0);
|
||||
res.body.rows.forEach(function (row) {
|
||||
row.should.have.keys('key', 'count', 'value');
|
||||
row.value.should.have.keys('name', 'description');
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -53,11 +53,11 @@ describe('controllers/web/package.test.js', function () {
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('rows');
|
||||
res.body.rows.should.eql([
|
||||
{ key: 'c', count: 1, value: { name: 'c', description: 'Give folders or directories comments and view them easy.' } },
|
||||
{ key: 'charset', count: 1,
|
||||
value: { name: 'charset', description: 'Get the content charset from header and html content-type.' } }
|
||||
]);
|
||||
res.body.rows.length.should.above(0);
|
||||
res.body.rows.forEach(function (row) {
|
||||
row.should.have.keys('key', 'count', 'value');
|
||||
row.value.should.have.keys('name', 'description');
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -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) {
|
||||
@@ -129,6 +134,13 @@ describe('controllers/web/package.test.js', function () {
|
||||
.expect(/Packages match/, done);
|
||||
});
|
||||
|
||||
it('should list by keyword with json ok', function (done) {
|
||||
request(app)
|
||||
.get('/browse/keyword/cnpm?type=json')
|
||||
.expect(200)
|
||||
.expect('content-type', 'application/json; charset=utf-8', done);
|
||||
});
|
||||
|
||||
it('should list no match ok', function (done) {
|
||||
request(app)
|
||||
.get('/browse/keyword/notexistpackage')
|
||||
@@ -141,7 +153,7 @@ describe('controllers/web/package.test.js', function () {
|
||||
request(app)
|
||||
.get('/browse/keyword/notexistpackage')
|
||||
.expect(500)
|
||||
.expect(/MockError: mm mock error/, done);
|
||||
.expect(/Internal Server Error/, done);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
7
test/fixtures/500.txt
vendored
Normal file
7
test/fixtures/500.txt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
Internal routing error
|
||||
|
||||
Sorry, we cannot connect to the intended server.
|
||||
|
||||
We have just been notified of this problem. We will correct it as soon as possible.
|
||||
|
||||
Feel free to contact us if you have any questions: support@iriscouch.com
|
||||
1
test/fixtures/package_and_tgz.json
vendored
Normal file
1
test/fixtures/package_and_tgz.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"_id":"mk2testmodule","name":"mk2testmodule","description":"","dist-tags":{"latest":"0.0.1"},"versions":{"0.0.1":{"name":"mk2testmodule","version":"0.0.1","description":"","main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"author":"","license":"ISC","readme":"ERROR: No README data found!","_id":"mk2testmodule@0.0.1","dist":{"shasum":"fa475605f88bab9b1127833633ca3ae0a477224c","tarball":"http://127.0.0.1:7001/mk2testmodule/-/mk2testmodule-0.0.1.tgz"},"_from":".","_npmVersion":"1.4.3","_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}]}},"readme":"ERROR: No README data found!","maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_attachments":{"mk2testmodule-0.0.1.tgz":{"content_type":"application/octet-stream","data":"H4sIAAAAAAAAA+2SsWrDMBCGPfspDg2ZinOyEgeylg6Zu2YR8rVRHEtGkkOg5N0jWaFdujVQAv6W4/7/dHcSGqTq5Ccthxyro7emeDCI2KxWkOKmaaaIdc4TouZQ8FqgwI3AdVMgF8ijho9e5DdGH6SLq/y1T74LfMcn4asEYEb2xLbA+q4O5ENv2/FE7CVZZ3JeW5NcrLDiWW3JK6eHcHey2Es9Zdq0dIkfKau50EcjjYpCmpDKSB0s7Nmbc9ZtwVhIBviBlP7Q1O4ZLBZAFx2As3jyOnWTYzhY9zPzpBUZPy2/e39l5bX87wedmZmZeRJuheTX2wAIAAA=","length":251}}}
|
||||
@@ -45,11 +45,12 @@ describe('middleware/auth.test.js', function () {
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it('should 401 with authorization and check fail', function (done) {
|
||||
it('should pass with authorization and check fail', function (done) {
|
||||
// npm install no need to check auth
|
||||
request(app)
|
||||
.get('/-/user/org.couchdb.user:cnpmjstest10')
|
||||
.set('authorization', 'basic ' + new Buffer('cnpmjstest10:cnpmjstest').toString('base64'))
|
||||
.expect(401, done);
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it('should 500 with authorization and mysql error', function (done) {
|
||||
|
||||
@@ -30,8 +30,8 @@ describe('middleware/opensearch.test.js', function () {
|
||||
it('should get 200', function (done) {
|
||||
request(app)
|
||||
.get('/opensearch.xml')
|
||||
.set('host', 'localhost:7002')
|
||||
.expect(/http:\/\/localhost:7002/, done);
|
||||
.set('host', 'localhost')
|
||||
.expect(/http:\/\/localhost/, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@ var Module = require('../../proxy/module');
|
||||
|
||||
var fixtures = path.join(path.dirname(__dirname), 'fixtures');
|
||||
|
||||
var id;
|
||||
describe('proxy/module.test.js', function () {
|
||||
afterEach(mm.restore);
|
||||
|
||||
@@ -36,6 +37,79 @@ describe('proxy/module.test.js', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('search()', function () {
|
||||
it('should search modules', function (done) {
|
||||
Module.search('as', function (err, data) {
|
||||
should.not.exist(err);
|
||||
data.should.have.keys('keywordMatchs', 'searchMatchs');
|
||||
data.searchMatchs.length.should.above(0);
|
||||
data.searchMatchs.forEach(function (row) {
|
||||
row.should.have.keys('name', 'description');
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should search match keywords modules', function (done) {
|
||||
Module.search('aa', function (err, data) {
|
||||
should.not.exist(err);
|
||||
data.should.have.keys('keywordMatchs', 'searchMatchs');
|
||||
data.keywordMatchs.length.should.above(0);
|
||||
data.keywordMatchs.forEach(function (row) {
|
||||
row.should.have.keys('name', 'description');
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should search return empty', function (done) {
|
||||
Module.search('emptyemptyemptyempty', function (err, data) {
|
||||
should.not.exist(err);
|
||||
data.should.eql({
|
||||
keywordMatchs: [],
|
||||
searchMatchs: []
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('addKeywords()', function () {
|
||||
var mockName = 'aa' + Date.now();
|
||||
|
||||
after(function (done) {
|
||||
mysql.query('DELETE FROM module_keyword WHERE name=?', [mockName], done);
|
||||
});
|
||||
|
||||
it('should add diff keywords to module', function (done) {
|
||||
Module.addKeywords(mockName, mockName, ['aa', 'bb', 'cc'], function (err, results) {
|
||||
should.not.exist(err);
|
||||
results.should.be.an.Array;
|
||||
results.should.length(3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should add same keywords to module', function (done) {
|
||||
Module.addKeywords('aa', 'desc aa', ['aa', 'bb', 'cc'], function (err, results) {
|
||||
should.not.exist(err);
|
||||
results.should.be.an.Array;
|
||||
results.should.length(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getKeywords()', function () {
|
||||
it('should get aa module keywords', function (done) {
|
||||
Module.getKeywords('aa', function (err, keywords) {
|
||||
should.not.exist(err);
|
||||
keywords.should.eql(['aa', 'bb', 'cc']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('add()', function () {
|
||||
it('should success ad he@0.3.6', function (done) {
|
||||
var sourcePackage = require('../fixtures/0.3.6.json');
|
||||
@@ -53,6 +127,7 @@ describe('proxy/module.test.js', function () {
|
||||
};
|
||||
mod.package.dist = dist;
|
||||
Module.add(mod, function (err, result) {
|
||||
id = result.id;
|
||||
should.not.exist(err);
|
||||
Module.getById(result.id, function (err, row) {
|
||||
should.not.exist(err);
|
||||
@@ -74,4 +149,17 @@ describe('proxy/module.test.js', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateReadme()', function () {
|
||||
it('should update ok', function (done) {
|
||||
Module.updateReadme(id, 'test', function (err, data) {
|
||||
should.not.exist(err);
|
||||
Module.getById(id, function (err, data) {
|
||||
should.not.exist(err);
|
||||
data.package.readme.should.equal('test');
|
||||
done();
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
58
test/proxy/module_deps.test.js
Normal file
58
test/proxy/module_deps.test.js
Normal file
@@ -0,0 +1,58 @@
|
||||
/**!
|
||||
* cnpmjs.org - test/proxy/module_deps.test.js
|
||||
*
|
||||
* Copyright(c) 2014
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var should = require('should');
|
||||
var pedding = require('pedding');
|
||||
var ModuleDeps = require('../../proxy/module_deps');
|
||||
|
||||
describe('proxy/module_deps.test.js', function () {
|
||||
before(function (done) {
|
||||
done = pedding(2, done);
|
||||
ModuleDeps.remove('testmodule', 'foo', done);
|
||||
ModuleDeps.remove('testmodule', 'bar', done);
|
||||
});
|
||||
|
||||
describe('add()', function () {
|
||||
it('should add foo, bar success', function (done) {
|
||||
done = pedding(2, done);
|
||||
ModuleDeps.add('testmodule', 'foo', function (err) {
|
||||
should.not.exist(err);
|
||||
// add again should work
|
||||
ModuleDeps.add('testmodule', 'foo', function (err) {
|
||||
should.not.exist(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
ModuleDeps.add('testmodule', 'bar', done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('list()', function () {
|
||||
it('should list testmodule deps', function (done) {
|
||||
ModuleDeps.list('testmodule', function (err, rows) {
|
||||
should.not.exist(err);
|
||||
should.exist(rows);
|
||||
rows.should.be.an.Array;
|
||||
rows.should.length(2);
|
||||
rows.forEach(function (row) {
|
||||
row.should.have.property('deps');
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -16,8 +16,12 @@
|
||||
|
||||
var should = require('should');
|
||||
var mm = require('mm');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var npm = require('../../proxy/npm');
|
||||
|
||||
var fixtures = path.join(path.dirname(__dirname), 'fixtures');
|
||||
|
||||
describe('proxy/npm.test.js', function () {
|
||||
afterEach(mm.restore);
|
||||
|
||||
@@ -47,4 +51,16 @@ describe('proxy/npm.test.js', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return ServerError when http 500 response', function (done) {
|
||||
var content = fs.readFileSync(path.join(fixtures, '500.txt'), 'utf8');
|
||||
mm.http.request(/\//, content, { statusCode: 500 });
|
||||
// http://registry.npmjs.org/octopie
|
||||
npm.get('octopie', function (err, data) {
|
||||
should.exist(err);
|
||||
err.name.should.equal('NPMServerError');
|
||||
err.message.should.equal('Status 500, ' + content);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
table.downloads td.count {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.nav-tabs{margin:20px 0;}
|
||||
.nav-cont{display:none;}
|
||||
.nav-cont.active{display:block;}
|
||||
</style>
|
||||
<script>
|
||||
$(function () {
|
||||
@@ -41,13 +45,23 @@
|
||||
return location.href = '/browse/keyword/' + val;
|
||||
}
|
||||
});
|
||||
|
||||
$(".nav-tabs li").each(function (index) {
|
||||
$(this).data("index", index);
|
||||
})
|
||||
.on("click", function (e) {
|
||||
e.preventDefault();
|
||||
$(".nav-tabs li.active,.nav-cont.active").removeClass("active");
|
||||
$(this).addClass("active");
|
||||
$(".nav-cont").eq($(this).data("index")).addClass("active");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div id="content-header">
|
||||
<a href="/"><img src="http://ww4.sinaimg.cn/large/69c1d4acgw1ebfly5kjlij208202oglr.jpg"></a>
|
||||
<a href="/"><img src="{{logoURL}}"></a>
|
||||
<input type="text" id="search-input" class="form-control" placeholder="Search Packages">
|
||||
</div>
|
||||
<div id="content-body"><%- locals.body %></div>
|
||||
|
||||
@@ -18,204 +18,231 @@
|
||||
<p class="description"><%= package.description %></p>
|
||||
<% } %>
|
||||
|
||||
<pre class="sh"><code>$ <%- config.npmClientName %> install <%= package.name %></code></pre>
|
||||
|
||||
<table class="downloads">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="count"><%- download.today %></td><td> downloads today</td>
|
||||
<td class="count"><%- download.thisweek %></td><td> downloads in this week</td>
|
||||
<td class="count"><%- download.thismonth %></td><td> downloads in this month</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="count"><%- download.lastday %></td><td> downloads in the last day</td>
|
||||
<td class="count"><%- download.lastweek %></td><td> downloads in the last week</td>
|
||||
<td class="count"><%- download.lastmonth %></td><td> downloads in the last month</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="metadata">
|
||||
<% if (Array.isArray(package.maintainers) && package.maintainers.length > 0) { %>
|
||||
<tr>
|
||||
<th>Maintainers</th>
|
||||
<td>
|
||||
<% package.maintainers.forEach(function (m) { %>
|
||||
<span class="user">
|
||||
<a class="username" href="/~<%= m.name %>">
|
||||
<% if (m.gravatar) { %>
|
||||
<img src="<%- m.gravatar %>" class="avatar">
|
||||
<% } %>
|
||||
<%= m.name %>
|
||||
</a>
|
||||
</span>
|
||||
<% }) %>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
<tr>
|
||||
<th>Version</th>
|
||||
<td>
|
||||
<b>
|
||||
<%= package.version %>
|
||||
</b>
|
||||
<% if (package.fromNow) { %>
|
||||
last updated
|
||||
<%= package.fromNow %>
|
||||
<% } %>
|
||||
</td>
|
||||
</tr>
|
||||
<% if (package.license) { %>
|
||||
<tr>
|
||||
<th>License</th>
|
||||
<td>
|
||||
<a href="<%= package.license.url %>" target="_blank"><%= package.license.name %></a>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
<%
|
||||
if (typeof package.keywords === 'string') {
|
||||
package.keywords = package.keywords.split(/\s*,?\s*/)
|
||||
}
|
||||
if (Array.isArray(package.keywords)) {
|
||||
%>
|
||||
<tr>
|
||||
<th>Keywords</th>
|
||||
<td><%-
|
||||
package.keywords.map(function (kw) {
|
||||
kw = kw.replace(/</g, '<').replace(/"/g, '"')
|
||||
return '<a href="/browse/keyword/' + kw + '">' + kw + '</a>'
|
||||
}).join(', ')
|
||||
%></td>
|
||||
</tr>
|
||||
<% } %>
|
||||
<% if (package.repository && package.repository !== 'undefined') {
|
||||
var gh = package.repository.url &&
|
||||
package.repository.url.match(
|
||||
/^(?:https?:\/\/|git(?::\/\/|@))(gist.github.com|github.com)[:\/](.*?)(?:.git)?$/)
|
||||
if (gh) {
|
||||
gh = 'https://' + gh[1] + '/' + gh[2]
|
||||
}
|
||||
%>
|
||||
<tr>
|
||||
<th>Repository</td>
|
||||
<td>
|
||||
<% if (gh) { %><a href="<%= gh %>" target="_blank"><% } %>
|
||||
<%= package.repository.url %><% if (gh) { %></a><% } %>
|
||||
(<%= package.repository.type %>)
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
<% if (typeof package.homepage === 'string') { %>
|
||||
<tr>
|
||||
<th>Homepage</td>
|
||||
<td>
|
||||
<a href="<%= encodeURI(package.homepage) %>" target="_blank"><%= package.homepage.replace(/</g, '<') %></a>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
<% if (package.bugs && package.bugs.url) { %>
|
||||
<tr>
|
||||
<th>Bugs</td>
|
||||
<td>
|
||||
<a href="<%= package.bugs.url %>" target="_blank"><%= package.bugs.url %></a>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
<tr>
|
||||
<%
|
||||
var deps = Object.keys(package.dependencies || {})
|
||||
var l = deps.length
|
||||
%>
|
||||
<th>Dependencies<%= l > 5 ? ' (' + l + ')' : '' %></th>
|
||||
<td>
|
||||
<%
|
||||
if (l === 0) {
|
||||
%>None<%
|
||||
} else {
|
||||
var m = 200
|
||||
if (l > m) deps = deps.slice(0, m)
|
||||
deps.forEach(function(pkg, i) {
|
||||
if (i > 0) { %>, <% }
|
||||
%>
|
||||
<a href="/package/<%= pkg %>"><%= pkg %></a><%
|
||||
})
|
||||
if (l > m) {
|
||||
%>, and <%= l-m %> more<%
|
||||
}
|
||||
}
|
||||
%>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<% if (package.users) {
|
||||
var starredBy = package.starredBy
|
||||
var l = starredBy.length
|
||||
%>
|
||||
<tr>
|
||||
<th>Starred by<%= l > 5 ? ' (' + l + ')' : '' %></th>
|
||||
<td>
|
||||
<%
|
||||
var max = 20
|
||||
if (l > max) {
|
||||
starredBy = starredBy.sort(function (a, b) {
|
||||
return Math.random() * 2 - 1
|
||||
}).slice(0, max)
|
||||
}
|
||||
starredBy.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><%
|
||||
}
|
||||
%>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
|
||||
<% if (Array.isArray(package.contributors) && package.contributors.length > 0) { %>
|
||||
<tr>
|
||||
<th>Contributors</th>
|
||||
<td>
|
||||
<% package.contributors.forEach(function (m) { %>
|
||||
<span class="user">
|
||||
<a class="username" target="_blank" href="<%= m.url %>">
|
||||
<% if (m.gravatar) { %>
|
||||
<img src="<%- m.gravatar %>" class="avatar">
|
||||
<% } %>
|
||||
<%= m.name %>
|
||||
</a>
|
||||
</span>
|
||||
<% }) %>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
|
||||
<% if (package.dist && package.dist.tarball) { %>
|
||||
<tr>
|
||||
<th>Download</th>
|
||||
<td>
|
||||
<a class="downloadlink" target="_blank" href="<%= package.dist.tarball %>">
|
||||
<%= package.dist.tarball %>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
</table>
|
||||
|
||||
<div class="details">
|
||||
<ul class="toc">
|
||||
<% if (package.readme) { %>
|
||||
<li><a href="#readme">Read Me</a></li>
|
||||
<% } %>
|
||||
</ul>
|
||||
<pre class="sh"><code>$ <%- config.npmClientName %> install <%= package.name %> <% if (package.preferGlobal) { %>-g<% } %></code></pre>
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<% if (package.readme) { %>
|
||||
<section id="readme">
|
||||
<%- package.readme %>
|
||||
</section>
|
||||
<li class="active"><a href="#readme">Readme.md</a></li>
|
||||
<li><a href="#meta">package.json</a></li>
|
||||
<% } else { %>
|
||||
<li class="active"><a href="#meta">package.json</a></li>
|
||||
<% } %>
|
||||
</ul>
|
||||
|
||||
<% if (package.readme) { %>
|
||||
<div class="nav-cont active">
|
||||
<div class="details">
|
||||
<section id="readme">
|
||||
<%- package.readme %>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div class="nav-cont">
|
||||
<table class="downloads">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="count"><%- download.today %></td><td> downloads today</td>
|
||||
<td class="count"><%- download.thisweek %></td><td> downloads in this week</td>
|
||||
<td class="count"><%- download.thismonth %></td><td> downloads in this month</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="count"><%- download.lastday %></td><td> downloads in the last day</td>
|
||||
<td class="count"><%- download.lastweek %></td><td> downloads in the last week</td>
|
||||
<td class="count"><%- download.lastmonth %></td><td> downloads in the last month</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="metadata">
|
||||
<% if (Array.isArray(package.maintainers) && package.maintainers.length > 0) { %>
|
||||
<tr>
|
||||
<th>Maintainers</th>
|
||||
<td>
|
||||
<% package.maintainers.forEach(function (m) { %>
|
||||
<span class="user">
|
||||
<a class="username" href="/~<%= m.name %>">
|
||||
<% if (m.gravatar) { %>
|
||||
<img src="<%- m.gravatar %>" class="avatar">
|
||||
<% } %>
|
||||
<%= m.name %>
|
||||
</a>
|
||||
</span>
|
||||
<% }) %>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
<tr>
|
||||
<th>Version</th>
|
||||
<td>
|
||||
<b>
|
||||
<%= package.version %>
|
||||
</b>
|
||||
<% if (package.fromNow) { %>
|
||||
last updated
|
||||
<%= package.fromNow %>
|
||||
<% } %>
|
||||
</td>
|
||||
</tr>
|
||||
<% if (package.license) { %>
|
||||
<tr>
|
||||
<th>License</th>
|
||||
<td>
|
||||
<a href="<%= package.license.url %>" target="_blank"><%= package.license.name %></a>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
<%
|
||||
if (typeof package.keywords === 'string') {
|
||||
package.keywords = package.keywords.split(/\s*,?\s*/)
|
||||
}
|
||||
if (Array.isArray(package.keywords)) {
|
||||
%>
|
||||
<tr>
|
||||
<th>Keywords</th>
|
||||
<td><%-
|
||||
package.keywords.map(function (kw) {
|
||||
kw = kw.replace(/</g, '<').replace(/"/g, '"')
|
||||
return '<a href="/browse/keyword/' + kw + '">' + kw + '</a>'
|
||||
}).join(', ')
|
||||
%></td>
|
||||
</tr>
|
||||
<% } %>
|
||||
<% if (package.repository && package.repository !== 'undefined') { %>
|
||||
<tr>
|
||||
<th>Repository</td>
|
||||
<td>
|
||||
<a href="<%= package.repository.weburl || package.repository.url %>" target="_blank">
|
||||
<%= package.repository.url %>
|
||||
</a>
|
||||
(<%= package.repository.type %>)
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
<% if (typeof package.homepage === 'string') { %>
|
||||
<tr>
|
||||
<th>Homepage</td>
|
||||
<td>
|
||||
<a href="<%= encodeURI(package.homepage) %>" target="_blank"><%= package.homepage.replace(/</g, '<') %></a>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
<% if (package.bugs && package.bugs.url) { %>
|
||||
<tr>
|
||||
<th>Bugs</td>
|
||||
<td>
|
||||
<a href="<%= package.bugs.url %>" target="_blank"><%= package.bugs.url %></a>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
<tr>
|
||||
<%
|
||||
var deps = Object.keys(package.dependencies || {})
|
||||
var l = deps.length
|
||||
%>
|
||||
<th>Dependencies<%= l > 0 ? ' (' + l + ')' : '' %></th>
|
||||
<td>
|
||||
<%
|
||||
if (l === 0) {
|
||||
%>None<%
|
||||
} else {
|
||||
var m = 200
|
||||
if (l > m) deps = deps.slice(0, m)
|
||||
deps.forEach(function(pkg, i) {
|
||||
if (i > 0) { %>, <% }
|
||||
%>
|
||||
<a href="/package/<%= pkg %>"><%= pkg %></a><%
|
||||
})
|
||||
if (l > m) {
|
||||
%>, and <%= l-m %> more<%
|
||||
}
|
||||
}
|
||||
%>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<%
|
||||
var deps = package.dependents || [];
|
||||
var l = deps.length
|
||||
%>
|
||||
<th>Dependents<%= l > 0 ? ' (' + l + ')' : '' %></th>
|
||||
<td>
|
||||
<%
|
||||
if (l === 0) {
|
||||
%>None<%
|
||||
} else {
|
||||
var m = 200
|
||||
if (l > m) deps = deps.slice(0, m)
|
||||
deps.forEach(function(pkg, i) {
|
||||
if (i > 0) { %>, <% }
|
||||
%>
|
||||
<a href="/package/<%= pkg %>"><%= pkg %></a><%
|
||||
})
|
||||
if (l > m) {
|
||||
%>, and <%= l-m %> more<%
|
||||
}
|
||||
}
|
||||
%>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<% if (package.users) {
|
||||
var starredBy = package.starredBy
|
||||
var l = starredBy.length
|
||||
%>
|
||||
<tr>
|
||||
<th>Starred by<%= l > 0 ? ' (' + l + ')' : '' %></th>
|
||||
<td>
|
||||
<%
|
||||
var max = 20
|
||||
if (l > max) {
|
||||
starredBy = starredBy.sort(function (a, b) {
|
||||
return Math.random() * 2 - 1
|
||||
}).slice(0, max)
|
||||
}
|
||||
starredBy.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><%
|
||||
}
|
||||
%>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
|
||||
<% if (Array.isArray(package.contributors) && package.contributors.length > 0) { %>
|
||||
<tr>
|
||||
<th>Contributors</th>
|
||||
<td>
|
||||
<% package.contributors.forEach(function (m) { %>
|
||||
<span class="user">
|
||||
<a class="username" target="_blank" href="<%= m.url %>">
|
||||
<% if (m.gravatar) { %>
|
||||
<img src="<%- m.gravatar %>" class="avatar">
|
||||
<% } %>
|
||||
<%= m.name %>
|
||||
</a>
|
||||
</span>
|
||||
<% }) %>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
|
||||
<% if (package.dist && package.dist.tarball) { %>
|
||||
<tr>
|
||||
<th>Download</th>
|
||||
<td>
|
||||
<a class="downloadlink" target="_blank" href="<%= package.dist.tarball %>">
|
||||
<%= package.dist.tarball %>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,26 +16,43 @@
|
||||
<div id="search">
|
||||
<% if (!packages.length) { %>
|
||||
<div class="alert alert-warning">
|
||||
Can not found package match <%= keyword %>. You can
|
||||
Can not found package match <%= keyword %>. You can
|
||||
<a href="/sync/<%= keyword %>" target="_blank">SYNC</a> from official npm registry or
|
||||
<a href="https://npmjs.org/search?q=<%= keyword %>" target="_blank">SEARCH</a> in official npm website.
|
||||
</div>
|
||||
<% } else if (packages[0].name !== keyword) { %>
|
||||
<div class="alert alert-info">
|
||||
Can not found package <%= keyword %>. You can
|
||||
Can not found package <%= keyword %>. You can
|
||||
<a href="/sync/<%= keyword %>" target="_blank">SYNC</a> from official npm registry or
|
||||
<a href="https://npmjs.org/search?q=<%= keyword %>" target="_blank">SEARCH</a> in official npm website.
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
<% if (packages.length) { %>
|
||||
<h1>
|
||||
Packages match <span style="color: #09f;"><%= keyword %></span>
|
||||
Packages match "<span style="color: #09f;"><%= keyword %></span>"
|
||||
</h1>
|
||||
<hr />
|
||||
<% for (var i = 0; i < packages.length; i++) { %>
|
||||
<div class="package <%= packages[i].name === keyword ? 'match' : '' %>">
|
||||
<a href="/package/<%= packages[i].name %>" class="package-name"><%= packages[i].name %></a>
|
||||
<span class="package-description"><%= packages[i].description %></span>
|
||||
<% for (var i = 0; i < packages.length; i++) {
|
||||
var item = packages[i];
|
||||
%>
|
||||
<div class="package <%= item.name === keyword ? 'match' : '' %>">
|
||||
<a href="/package/<%= item.name %>" class="package-name"><%= item.name %></a>
|
||||
<span class="package-description"><%= item.description %></span>
|
||||
</div>
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
<% if (keywords.length) { %>
|
||||
<h1>
|
||||
Keywords match "<span style="color: #09f;"><%= keyword %></span>"
|
||||
</h1>
|
||||
<hr />
|
||||
<% for (var i = 0; i < keywords.length; i++) {
|
||||
var item = keywords[i];
|
||||
%>
|
||||
<div class="package <%= item.name === keyword ? 'match' : '' %>">
|
||||
<a href="/package/<%= item.name %>" class="package-name"><%= item.name %></a>
|
||||
<span class="package-description"><%= item.description %></span>
|
||||
</div>
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - worker.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
var graceful = require('graceful');
|
||||
|
||||
var logger = require('./common/logger');
|
||||
var config = require('./config');
|
||||
var registry = require('./servers/registry');
|
||||
var web = require('./servers/web');
|
||||
@@ -34,6 +34,7 @@ graceful({
|
||||
if (err.message) {
|
||||
err.message += ' (uncaughtException throw ' + throwErrorCount + ' times on pid:' + process.pid + ')';
|
||||
}
|
||||
console.error(err);
|
||||
console.error(err.stack);
|
||||
logger.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user