Compare commits
111 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
9445d46bbf | ||
|
|
55bfcc853e | ||
|
|
4eeb1b25de | ||
|
|
42959196c6 | ||
|
|
d264b47b09 | ||
|
|
4e412d1808 | ||
|
|
73527da1f1 | ||
|
|
7986f198af | ||
|
|
adb1411313 | ||
|
|
2f24e9828e | ||
|
|
d7e5921e24 | ||
|
|
e3e6a1aaa5 | ||
|
|
0d1969fadd | ||
|
|
028e599d51 | ||
|
|
fa0dd5c23f | ||
|
|
7be5df8e70 | ||
|
|
adddf0e4c5 | ||
|
|
28cc13d583 | ||
|
|
c85b27b9b2 | ||
|
|
46795adf54 | ||
|
|
c8ab1735a5 | ||
|
|
4c759b40c8 | ||
|
|
1bab099f38 | ||
|
|
f95f814a8c | ||
|
|
ffcb0d669a | ||
|
|
81ca81d578 | ||
|
|
803f6d42f8 | ||
|
|
5366f16bcb | ||
|
|
ab18e49131 | ||
|
|
3c874244a7 | ||
|
|
d463b09c81 | ||
|
|
40301f260b | ||
|
|
72dbde1906 | ||
|
|
d68157faa8 | ||
|
|
c88991c028 | ||
|
|
262abe8520 | ||
|
|
9dd942df1a | ||
|
|
9ec552e08d | ||
|
|
2420164a9d | ||
|
|
822f2f6a4e | ||
|
|
d423a987ae | ||
|
|
70cefd817e | ||
|
|
2606af24a1 | ||
|
|
5b781cbda1 | ||
|
|
1519b52a74 | ||
|
|
4b73f6e0a7 | ||
|
|
88bcb14a4f | ||
|
|
5bd2cf5d50 | ||
|
|
e8fcfc67a8 | ||
|
|
580fc8c777 | ||
|
|
60fdcdd0e4 | ||
|
|
09fdff49a1 | ||
|
|
2f1b5034c4 | ||
|
|
6571ec53bb | ||
|
|
4a106f1bdf | ||
|
|
92c20c90ac | ||
|
|
31a920c5b3 | ||
|
|
a3b51c6803 | ||
|
|
2482c06e04 | ||
|
|
bbafa027e8 | ||
|
|
3ca62486d2 | ||
|
|
6371d60b70 | ||
|
|
8014e0281f | ||
|
|
253c11c855 | ||
|
|
49c5c73281 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -19,3 +19,6 @@ config/config.js
|
||||
backup/*.json
|
||||
backup/*.gz
|
||||
docs/web/history.md
|
||||
view/web/_layout.html
|
||||
bin/mysql.js
|
||||
bin/test.sql
|
||||
|
||||
@@ -8,3 +8,5 @@ logo.png
|
||||
public/dist/
|
||||
backup/*.json
|
||||
backup/*.gz
|
||||
view/web/_layout.html
|
||||
bin/mysql.js
|
||||
|
||||
7
AUTHORS
7
AUTHORS
@@ -1,7 +1,8 @@
|
||||
# Ordered by date of first contribution.
|
||||
# Auto-generated by 'contributors' on Fri, 27 Dec 2013 06:55:24 GMT.
|
||||
# Auto-generated by 'contributors' on Fri, 24 Jan 2014 08:35:59 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)
|
||||
|
||||
153
History.md
153
History.md
@@ -1,4 +1,157 @@
|
||||
|
||||
0.2.6 / 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
|
||||
==================
|
||||
|
||||
* add make autod
|
||||
* update publish_time, fixed #163
|
||||
|
||||
0.2.13 / 2014-01-15
|
||||
==================
|
||||
|
||||
* markdown tmpl not support footer, need to wrap on app start
|
||||
|
||||
0.2.12 / 2014-01-15
|
||||
==================
|
||||
|
||||
* add footer and npm client name customable
|
||||
|
||||
0.2.11 / 2014-01-15
|
||||
==================
|
||||
|
||||
* package page contributor link to search, default is true
|
||||
|
||||
0.2.10 / 2014-01-14
|
||||
==================
|
||||
|
||||
* fix #155 Content-Disposition wrong.
|
||||
|
||||
0.2.9 / 2014-01-14
|
||||
==================
|
||||
|
||||
* support startkey=c and startkey="c"
|
||||
* support couch db search api. fixed #153
|
||||
* fix fork me image link
|
||||
* support sync by query.name
|
||||
|
||||
0.2.8 / 2014-01-14
|
||||
==================
|
||||
|
||||
* dont show err stack on test env
|
||||
* add download link for package page
|
||||
|
||||
0.2.7 / 2014-01-13
|
||||
==================
|
||||
|
||||
* add shasum when nfs.upload and hfs.uploadBuffer, fixed #148
|
||||
|
||||
0.2.6 / 2014-01-13
|
||||
==================
|
||||
|
||||
* support custom session store, fixed #146
|
||||
|
||||
0.2.5 / 2014-01-13
|
||||
==================
|
||||
|
||||
* add download timeout and unit test
|
||||
* use downloadStream() first
|
||||
* nfs download to a writeable stream.
|
||||
|
||||
0.2.4 / 2014-01-10
|
||||
==================
|
||||
|
||||
* set main script to index.js, fixed #142
|
||||
|
||||
0.2.3 / 2014-01-10
|
||||
==================
|
||||
|
||||
* Dont show sync button on private package
|
||||
* Sync package as publish with no deps. fixed #138
|
||||
|
||||
0.2.2 / 2014-01-10
|
||||
==================
|
||||
|
||||
* keep compatibility
|
||||
* qnfs upload only callback a url
|
||||
* compat remove package
|
||||
* set tarball url
|
||||
* new npm publish in one request, add _publish_in_cnpm
|
||||
* support unsure name ufs
|
||||
* contributors maybe a object
|
||||
* Object #<Object> has no method 'forEach' fixed #134
|
||||
* support custom config as a module, fixed issue #132
|
||||
* support npm new publish flow. fixed #129
|
||||
* add toString and constructor to test admin
|
||||
* fix #119 hasOwnProperty check admin bug.
|
||||
|
||||
0.2.0 / 2013-12-27
|
||||
==================
|
||||
|
||||
|
||||
5
Makefile
5
Makefile
@@ -10,6 +10,7 @@ test: install
|
||||
@NODE_ENV=test ./node_modules/mocha/bin/mocha \
|
||||
--reporter $(REPORTER) \
|
||||
--timeout $(TIMEOUT) \
|
||||
--require should \
|
||||
$(MOCHA_OPTS) \
|
||||
$(TESTS)
|
||||
|
||||
@@ -30,4 +31,8 @@ test-all: test test-cov
|
||||
contributors: install
|
||||
@./node_modules/contributors/bin/contributors -f plain -o AUTHORS
|
||||
|
||||
autod: install
|
||||
@./node_modules/.bin/autod -w -e public,view,docs,backup
|
||||
@$(MAKE) install
|
||||
|
||||
.PHONY: test
|
||||
|
||||
27
README.md
27
README.md
@@ -1,20 +1,21 @@
|
||||
cnpmjs.org
|
||||
=======
|
||||
|
||||
[](http://travis-ci.org/fengmk2/cnpmjs.org) [](https://coveralls.io/r/fengmk2/cnpmjs.org)[](https://gemnasium.com/fengmk2/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/)
|
||||
|
||||

|
||||

|
||||
|
||||
## What is this?
|
||||
|
||||
Private npm registry and web for Enterprise, base on MySQL and Simple File Store.
|
||||
Private npm registry and web for Enterprise, base on 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).
|
||||
|
||||

|
||||
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
@@ -27,20 +28,26 @@ $ npm install
|
||||
$ node 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 : 2 weeks
|
||||
active : 81 days
|
||||
commits : 205
|
||||
files : 83
|
||||
repo age : 7 weeks
|
||||
active : 132 days
|
||||
commits : 315
|
||||
files : 88
|
||||
authors :
|
||||
120 fengmk2 58.5%
|
||||
84 dead_horse 41.0%
|
||||
1 Alsotang 0.5%
|
||||
190 fengmk2 60.3%
|
||||
122 dead_horse 38.7%
|
||||
2 4simple 0.6%
|
||||
1 Alsotang 0.3%
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
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();
|
||||
@@ -19,6 +19,8 @@ var config = require('../config');
|
||||
|
||||
var client = qn.create(config.qn);
|
||||
|
||||
exports._client = client;
|
||||
|
||||
/**
|
||||
* Upload file
|
||||
*
|
||||
@@ -32,7 +34,23 @@ var client = qn.create(config.qn);
|
||||
*/
|
||||
exports.upload = function (filepath, options, callback) {
|
||||
client.delete(options.key, function (err, data) {
|
||||
client.uploadFile(filepath, {key: options.key, size: options.size}, callback);
|
||||
client.uploadFile(filepath, {key: options.key, size: options.size}, function (err, data) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, {url: data.url});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.uploadBuffer = function (buf, options, callback) {
|
||||
client.delete(options.key, function (err, data) {
|
||||
client.upload(buf, {key: options.key}, function (err, data) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, {url: data.url});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ if (config.debug) {
|
||||
session = connect.session({
|
||||
key: key,
|
||||
secret: config.sessionSecret,
|
||||
store: new RedisStore(config.redis),
|
||||
store: config.sessionStore || new RedisStore(config.redis),
|
||||
cookie: cookie,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -68,9 +68,14 @@ var config = {
|
||||
user: 'address@gmail.com',
|
||||
pass: 'your password',
|
||||
ssl: true,
|
||||
debug: false
|
||||
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`
|
||||
packagePageContributorSearch: true, // package page contributor link to search, default is true
|
||||
sourceNpmRegistry: 'http://registry.npmjs.org',
|
||||
enablePrivate: true, // enable private mode, only admin can publish, other use just can sync package from source npm
|
||||
admins: {
|
||||
@@ -83,6 +88,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
|
||||
@@ -98,3 +104,12 @@ mkdirp.sync(config.logdir);
|
||||
mkdirp.sync(config.uploadDir);
|
||||
|
||||
module.exports = config;
|
||||
|
||||
config.loadConfig = function (customConfig) {
|
||||
if (!customConfig) {
|
||||
return;
|
||||
}
|
||||
for (var key in customConfig) {
|
||||
config[key] = customConfig[key];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -23,6 +23,9 @@ var utility = require('utility');
|
||||
var eventproxy = require('eventproxy');
|
||||
var Bagpipe = require('bagpipe');
|
||||
var urlparse = require('url').parse;
|
||||
var mime = require('mime');
|
||||
var semver = require('semver');
|
||||
var ms = require('ms');
|
||||
var config = require('../../config');
|
||||
var Module = require('../../proxy/module');
|
||||
var Total = require('../../proxy/total');
|
||||
@@ -32,7 +35,7 @@ var Log = require('../../proxy/module_log');
|
||||
var DownloadTotal = require('../../proxy/download');
|
||||
var SyncModuleWorker = require('../../proxy/sync_module_worker');
|
||||
var logger = require('../../common/logger');
|
||||
var semver = require('semver');
|
||||
var ModuleDeps = require('../../proxy/module_deps');
|
||||
|
||||
exports.show = function (req, res, next) {
|
||||
var name = req.params.name;
|
||||
@@ -83,7 +86,8 @@ exports.show = function (req, res, next) {
|
||||
continue;
|
||||
}
|
||||
var pkg = row.package;
|
||||
common.downloadURL(pkg, req);
|
||||
common.setDownloadURL(pkg, req);
|
||||
pkg._cnpm_publish_time = row.publish_time;
|
||||
versions[pkg.version] = pkg;
|
||||
times[pkg.version] = row.publish_time ? new Date(row.publish_time) : row.gmt_modified;
|
||||
if ((!distTags.latest && !latestMod) || distTags.latest === row.version) {
|
||||
@@ -129,7 +133,6 @@ exports.get = function (req, res, next) {
|
||||
var name = req.params.name;
|
||||
var tag = req.params.version;
|
||||
var version = semver.valid(tag);
|
||||
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(next);
|
||||
|
||||
@@ -138,7 +141,8 @@ exports.get = function (req, res, next) {
|
||||
|
||||
Module[method](name, queryLabel, ep.done(function (mod) {
|
||||
if (mod) {
|
||||
common.downloadURL(mod.package, req);
|
||||
common.setDownloadURL(mod.package, req);
|
||||
mod.package._cnpm_publish_time = mod.publish_time;
|
||||
return res.json(mod.package);
|
||||
}
|
||||
ep.emit('notFound');
|
||||
@@ -168,14 +172,86 @@ exports.get = function (req, res, next) {
|
||||
|
||||
var _downloads = {};
|
||||
|
||||
var DOWNLOAD_TIMEOUT = ms('10m');
|
||||
|
||||
exports.download = function (req, res, next) {
|
||||
var name = req.params.name;
|
||||
var filename = req.params.filename;
|
||||
var cdnurl = nfs.url(common.getCDNKey(name, filename));
|
||||
res.statusCode = 302;
|
||||
res.setHeader('Location', cdnurl);
|
||||
res.end();
|
||||
_downloads[name] = (_downloads[name] || 0) + 1;
|
||||
var version = filename.slice(name.length + 1, -4);
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(next);
|
||||
|
||||
Module.get(name, version, ep.doneLater('moduleInfo'));
|
||||
ep.once('moduleInfo', function (row) {
|
||||
if (!row || !row.package || !row.package.dist) {
|
||||
return ep.emit('nodist');
|
||||
}
|
||||
var dist = row.package.dist;
|
||||
if (dist.key) {
|
||||
return ep.emit('key', dist);
|
||||
} else {
|
||||
return ep.emit('url', dist);
|
||||
}
|
||||
ep.emit('nodist');
|
||||
});
|
||||
|
||||
ep.once('nodist', function () {
|
||||
if (!nfs.url) {
|
||||
return next();
|
||||
}
|
||||
ep.emit('url', nfs.url(common.getCDNKey(name, filename)));
|
||||
});
|
||||
|
||||
ep.once('url', function (dist) {
|
||||
res.statusCode = 302;
|
||||
res.setHeader('Location', dist.tarball);
|
||||
res.end();
|
||||
_downloads[name] = (_downloads[name] || 0) + 1;
|
||||
});
|
||||
|
||||
ep.once('key', function (dist) {
|
||||
if (!nfs.downloadStream && !nfs.download) {
|
||||
return next();
|
||||
}
|
||||
|
||||
_downloads[name] = (_downloads[name] || 0) + 1;
|
||||
|
||||
if (typeof dist.size === 'number') {
|
||||
res.setHeader('Content-Length', dist.size);
|
||||
}
|
||||
res.setHeader('Content-Type', mime.lookup(dist.key));
|
||||
res.setHeader('Content-Disposition', 'attachment; filename="' + filename + '"');
|
||||
res.setHeader('ETag', dist.shasum);
|
||||
|
||||
if (nfs.downloadStream) {
|
||||
nfs.downloadStream(dist.key, res, {timeout: DOWNLOAD_TIMEOUT},
|
||||
function (err) {
|
||||
if (err) {
|
||||
// TODO: just end or send error response?
|
||||
return next(err);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// use download file api
|
||||
var tmpPath = path.join(config.uploadDir, utility.randomString() + dist.key);
|
||||
function cleanup() {
|
||||
fs.unlink(tmpPath, utility.noop);
|
||||
}
|
||||
|
||||
nfs.download(dist.key, tmpPath, {timeout: DOWNLOAD_TIMEOUT},
|
||||
function (err) {
|
||||
if (err) {
|
||||
cleanup();
|
||||
return next(err);
|
||||
}
|
||||
var tarball = fs.createReadStream(tmpPath);
|
||||
tarball.on('error', cleanup);
|
||||
tarball.on('end', cleanup);
|
||||
tarball.pipe(res);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
setInterval(function () {
|
||||
@@ -241,10 +317,8 @@ exports.upload = function (req, res, next) {
|
||||
if (err || !mod) {
|
||||
return next(err);
|
||||
}
|
||||
var match = mod.package.maintainers.filter(function (item) {
|
||||
return item.name === username;
|
||||
});
|
||||
if (match.length === 0 || mod.name !== name) {
|
||||
|
||||
if (!common.isMaintainer(req, mod.package.maintainers) || mod.name !== name) {
|
||||
return res.json(403, {
|
||||
error: 'no_perms',
|
||||
reason: 'Current user can not publish this module'
|
||||
@@ -276,8 +350,13 @@ exports.upload = function (req, res, next) {
|
||||
});
|
||||
}
|
||||
shasum = shasum.digest('hex');
|
||||
var key = common.getCDNKey(name, filename);
|
||||
nfs.upload(filepath, {key: key, size: length}, function (err, result) {
|
||||
|
||||
var options = {
|
||||
key: common.getCDNKey(name, filename),
|
||||
size: length,
|
||||
shasum: shasum
|
||||
};
|
||||
nfs.upload(filepath, options, function (err, result) {
|
||||
// remove tmp file whatever
|
||||
fs.unlink(filepath, utility.noop);
|
||||
if (err) {
|
||||
@@ -285,10 +364,18 @@ exports.upload = function (req, res, next) {
|
||||
}
|
||||
|
||||
var dist = {
|
||||
tarball: result.url,
|
||||
shasum: shasum,
|
||||
size: length
|
||||
};
|
||||
|
||||
// if nfs upload return a key, record it
|
||||
if (result.url) {
|
||||
dist.tarball = result.url;
|
||||
} else if (result.key) {
|
||||
dist.key = result.key;
|
||||
dist.tarball = result.key;
|
||||
}
|
||||
|
||||
mod.package.dist = dist;
|
||||
mod.package.version = version;
|
||||
debug('%s module: save file to %s, size: %d, sha1: %s, dist: %j, version: %s',
|
||||
@@ -304,6 +391,18 @@ exports.upload = function (req, res, next) {
|
||||
});
|
||||
};
|
||||
|
||||
function _addDepsRelations(pkg) {
|
||||
var dependencies = Object.keys(pkg.dependencies || {});
|
||||
if (dependencies.length > config.maxDependencies) {
|
||||
dependencies = dependencies.slice(0, config.maxDependencies);
|
||||
}
|
||||
|
||||
// add deps relations
|
||||
dependencies.forEach(function (depName) {
|
||||
ModuleDeps.add(depName, pkg.name, utility.noop);
|
||||
});
|
||||
}
|
||||
|
||||
exports.updateLatest = function (req, res, next) {
|
||||
var username = req.session.name;
|
||||
var name = req.params.name;
|
||||
@@ -351,10 +450,15 @@ exports.updateLatest = function (req, res, next) {
|
||||
}
|
||||
body._publish_on_cnpm = true;
|
||||
nextMod.package = body;
|
||||
_addDepsRelations(body);
|
||||
|
||||
// reset publish time
|
||||
nextMod.publish_time = Date.now();
|
||||
debug('update %s:%s %j', nextMod.package.name, nextMod.package.version, nextMod.package.dist);
|
||||
// change latest to version
|
||||
Module.update(nextMod, function (err) {
|
||||
if (err) {
|
||||
debug('update nextMod %s error: %s', name, err);
|
||||
return next(err);
|
||||
}
|
||||
// set latest tag
|
||||
@@ -375,21 +479,142 @@ exports.updateLatest = function (req, res, next) {
|
||||
});
|
||||
};
|
||||
|
||||
exports.addPackageAndDist = function (req, res, next) {
|
||||
// 'dist-tags': { latest: '0.0.2' },
|
||||
// _attachments:
|
||||
// { 'nae-sandbox-0.0.2.tgz':
|
||||
// { content_type: 'application/octet-stream',
|
||||
// data: 'H4sIAAAAA
|
||||
// length: 9883
|
||||
var pkg = req.body;
|
||||
var username = req.session.name;
|
||||
var name = req.params.name;
|
||||
var filename = Object.keys(pkg._attachments)[0];
|
||||
var attachment = pkg._attachments[filename];
|
||||
var version = filename.match(/\-([\.\d\w\_]+?)\.tgz$/);
|
||||
if (!version) {
|
||||
return res.json(403, {
|
||||
error: 'version_error',
|
||||
reason: filename + ' version not found',
|
||||
});
|
||||
}
|
||||
version = version[1];
|
||||
var versionPackage = pkg.versions[version];
|
||||
versionPackage._publish_on_cnpm = true;
|
||||
var distTags = pkg['dist-tags'] || {};
|
||||
var tags = []; // tag, version
|
||||
for (var t in distTags) {
|
||||
tags.push([t, distTags[t]]);
|
||||
}
|
||||
|
||||
debug('addPackageAndDist %s:%s, attachment size: %s', name, version, attachment.length);
|
||||
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(next);
|
||||
Module.get(name, version, ep.done('exists'));
|
||||
|
||||
var shasum;
|
||||
ep.on('exists', function (exists) {
|
||||
if (exists) {
|
||||
return res.json(409, {
|
||||
error: 'conflict',
|
||||
reason: 'Document update conflict.'
|
||||
});
|
||||
}
|
||||
|
||||
// upload attachment
|
||||
var tarballBuffer;
|
||||
try {
|
||||
tarballBuffer = new Buffer(attachment.data, 'base64');
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
}
|
||||
|
||||
if (tarballBuffer.length !== attachment.length) {
|
||||
return res.json(403, {
|
||||
error: 'size_wrong',
|
||||
reason: 'Attachment size ' + attachment.length + ' not match download size ' + tarballBuffer.length,
|
||||
});
|
||||
}
|
||||
|
||||
shasum = crypto.createHash('sha1');
|
||||
shasum.update(tarballBuffer);
|
||||
shasum = shasum.digest('hex');
|
||||
|
||||
var options = {
|
||||
key: common.getCDNKey(name, filename),
|
||||
shasum: shasum
|
||||
};
|
||||
nfs.uploadBuffer(tarballBuffer, options, ep.done('upload'));
|
||||
});
|
||||
|
||||
ep.on('upload', function (result) {
|
||||
debug('upload %j', result);
|
||||
|
||||
var dist = {
|
||||
shasum: shasum,
|
||||
size: attachment.length
|
||||
};
|
||||
|
||||
// if nfs upload return a key, record it
|
||||
if (result.url) {
|
||||
dist.tarball = result.url;
|
||||
} else if (result.key) {
|
||||
dist.key = result.key;
|
||||
dist.tarball = result.key;
|
||||
}
|
||||
|
||||
var mod = {
|
||||
name: name,
|
||||
version: version,
|
||||
author: username,
|
||||
package: versionPackage
|
||||
};
|
||||
|
||||
mod.package.dist = dist;
|
||||
_addDepsRelations(mod.package);
|
||||
|
||||
Module.add(mod, ep.done(function (r) {
|
||||
debug('%s module: save file to %s, size: %d, sha1: %s, dist: %j, version: %s',
|
||||
r.id, dist.tarball, dist.size, shasum, dist, version);
|
||||
ep.emit('saveModule', r.id);
|
||||
}));
|
||||
});
|
||||
|
||||
ep.on('saveModule', function () {
|
||||
if (tags.length === 0) {
|
||||
return ep.emit('saveTags');
|
||||
}
|
||||
|
||||
tags.forEach(function (item) {
|
||||
Module.addTag(name, item[0], item[1], ep.done('saveTag'));
|
||||
});
|
||||
ep.after('saveTag', tags.length, function () {
|
||||
ep.emit('saveTags');
|
||||
});
|
||||
});
|
||||
|
||||
ep.all('saveModule', 'saveTags', function (moduleId) {
|
||||
res.json(201, {ok: true, rev: String(moduleId)});
|
||||
});
|
||||
};
|
||||
|
||||
exports.add = function (req, res, next) {
|
||||
var username = req.session.name;
|
||||
var name = req.params.name;
|
||||
var pkg = req.body;
|
||||
var maintainers = pkg.maintainers || [];
|
||||
var match = maintainers.filter(function (item) {
|
||||
return item.name === username;
|
||||
});
|
||||
if (match.length === 0) {
|
||||
var pkg = req.body || {};
|
||||
|
||||
if (!common.isMaintainer(req, pkg.maintainers)) {
|
||||
return res.json(403, {
|
||||
error: 'no_perms',
|
||||
reason: 'Current user can not publish this module'
|
||||
});
|
||||
}
|
||||
|
||||
if (pkg._attachments && Object.keys(pkg._attachments).length > 0) {
|
||||
return exports.addPackageAndDist(req, res, next);
|
||||
}
|
||||
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(next);
|
||||
|
||||
@@ -413,6 +638,7 @@ exports.add = function (req, res, next) {
|
||||
maintainers: pkg.maintainers,
|
||||
},
|
||||
};
|
||||
debug('add next module: %s', name);
|
||||
Module.add(nextMod, ep.done(function (result) {
|
||||
nextMod.id = result.id;
|
||||
ep.emit('next', nextMod);
|
||||
@@ -420,19 +646,19 @@ exports.add = function (req, res, next) {
|
||||
}));
|
||||
|
||||
ep.all('latest', 'next', function (latestMod, nextMod) {
|
||||
var maintainers = latestMod ? latestMod.package.maintainers : nextMod.package.maintainers;
|
||||
var match = maintainers.filter(function (item) {
|
||||
return item.name === username;
|
||||
});
|
||||
var maintainers = latestMod && latestMod.package.maintainers.length > 0 ?
|
||||
latestMod.package.maintainers : nextMod.package.maintainers;
|
||||
|
||||
if (match.length === 0) {
|
||||
if (!common.isMaintainer(req, maintainers)) {
|
||||
return res.json(403, {
|
||||
error: 'no_perms',
|
||||
reason: 'Current user can not publish this module'
|
||||
});
|
||||
}
|
||||
|
||||
if (latestMod || nextMod.exists) {
|
||||
debug('add %s rev: %s, version: %s', name, nextMod.id, nextMod.version);
|
||||
|
||||
if (latestMod || nextMod.version !== 'next') {
|
||||
return res.json(409, {
|
||||
error: 'conflict',
|
||||
reason: 'Document update conflict.'
|
||||
@@ -455,37 +681,94 @@ exports.removeWithVersions = function (req, res, next) {
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(next);
|
||||
|
||||
// step1: list all the versions
|
||||
Module.listByName(name, ep.doneLater('list'));
|
||||
ep.once('list', function (mods) {
|
||||
if (!mods || !mods.length) {
|
||||
return next();
|
||||
}
|
||||
//TODO replace this maintainer check
|
||||
var match = mods[0].package.maintainers.filter(function (item) {
|
||||
return item.name === username;
|
||||
});
|
||||
|
||||
if (!match.length || mods[0].name !== name) {
|
||||
// step2: check permission
|
||||
var firstMod = mods[0];
|
||||
if (!common.isMaintainer(req, firstMod.package.maintainers) || firstMod.name !== name) {
|
||||
return res.json(403, {
|
||||
error: 'no_perms',
|
||||
reason: 'Current user can not update this module'
|
||||
});
|
||||
}
|
||||
|
||||
// step3: calculate which versions need to remove and
|
||||
// which versions need to remain
|
||||
var removeVersions = [];
|
||||
var removeVersionMaps = {};
|
||||
var remainVersions = [];
|
||||
|
||||
for (var i = 0; i < mods.length; i++) {
|
||||
var v = mods[i].version;
|
||||
if (v !== 'next' && !versions[v]) {
|
||||
if (v === 'next') {
|
||||
continue;
|
||||
}
|
||||
if (!versions[v]) {
|
||||
removeVersions.push(v);
|
||||
removeVersionMaps[v] = true;
|
||||
} else {
|
||||
remainVersions.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
if (!removeVersions.length) {
|
||||
return res.json(201, {ok: true});
|
||||
debug('no versions need to remove');
|
||||
return res.json(201, { ok: true });
|
||||
}
|
||||
Module.removeByNameAndVersions(name, removeVersions, ep.done(function () {
|
||||
res.json(201, {ok: true});
|
||||
debug('remove versions: %j, remain versions: %j', removeVersions, remainVersions);
|
||||
|
||||
// step 4: remove all the versions which need to remove
|
||||
Module.removeByNameAndVersions(name, removeVersions, ep.done('removeModules'));
|
||||
|
||||
// step5: list all the tags
|
||||
Module.listTags(name, ep.done(function (tags) {
|
||||
var removeTags = [];
|
||||
var latestRemoved = false;
|
||||
tags.forEach(function (tag) {
|
||||
// this tag need be removed
|
||||
if (removeVersionMaps[tag.version]) {
|
||||
removeTags.push(tag.id);
|
||||
if (tag.tag === 'latest') {
|
||||
latestRemoved = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!removeTags.length) {
|
||||
debug('no tag need be remove');
|
||||
ep.emit('removeTags');
|
||||
ep.emit('latest');
|
||||
return;
|
||||
}
|
||||
|
||||
debug('remove tags: %j', removeTags);
|
||||
// step 6: remove all the tags which need to remove
|
||||
Module.removeTagsByIds(removeTags, ep.done('removeTags'));
|
||||
|
||||
// step 7: check if latest tag being removed.
|
||||
// need generate a new latest tag
|
||||
if (!latestRemoved || !remainVersions[0]) {
|
||||
ep.emit('latest');
|
||||
} else {
|
||||
debug('latest tags removed, generate a new latest tag with new version: %s',
|
||||
remainVersions[0]);
|
||||
ep.emit('newLatestVersion', remainVersions[0]);
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
ep.all('newLatestVersion', 'removeTags', function (version) {
|
||||
Module.addTag(name, 'latest', version, ep.done('latest'));
|
||||
});
|
||||
|
||||
ep.all('removeModules', 'removeTags', 'latest', function () {
|
||||
return res.json(201, { ok: true });
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -503,18 +786,16 @@ exports.removeTar = function (req, res, next) {
|
||||
if (!mod) {
|
||||
return next();
|
||||
}
|
||||
//TODO replace this maintainer check
|
||||
var match = mod.package.maintainers.filter(function (item) {
|
||||
return item.name === username;
|
||||
});
|
||||
if (!match.length || mod.name !== name) {
|
||||
|
||||
if (!common.isMaintainer(req, mod.package.maintainers) || mod.name !== name) {
|
||||
return res.json(403, {
|
||||
error: 'no_perms',
|
||||
reason: 'Current user can not delete this tarball'
|
||||
});
|
||||
}
|
||||
var key = mod.package.dist && mod.package.dist.key;
|
||||
key = key || common.getCDNKey(mod.name, filename);
|
||||
|
||||
var key = common.getCDNKey(mod.name, filename);
|
||||
nfs.remove(key, ep.done(function () {
|
||||
res.json(200, {ok: true});
|
||||
}));
|
||||
@@ -532,15 +813,13 @@ exports.removeAll = function (req, res, next) {
|
||||
|
||||
Module.listByName(name, ep.doneLater('list'));
|
||||
ep.once('list', function (mods) {
|
||||
debug('removeAll module %s: %d', name, mods.length);
|
||||
var mod = mods[0];
|
||||
if (!mod) {
|
||||
return next();
|
||||
}
|
||||
//TODO replace this maintainer check
|
||||
var match = mod.package.maintainers.filter(function (item) {
|
||||
return item.name === username;
|
||||
});
|
||||
if (!match.length || mod.name !== name) {
|
||||
|
||||
if (!common.isMaintainer(req, mod.package.maintainers) || mod.name !== name) {
|
||||
return res.json(403, {
|
||||
error: 'no_perms',
|
||||
reason: 'Current user can not delete this tarball'
|
||||
@@ -559,7 +838,7 @@ exports.removeAll = function (req, res, next) {
|
||||
}
|
||||
var queue = new Bagpipe(5);
|
||||
keys.forEach(function (key) {
|
||||
queue.push(nfs.remove, key, function () {
|
||||
queue.push(nfs.remove.bind(nfs), key, function () {
|
||||
//ignore err here
|
||||
ep.emit('removeTar');
|
||||
});
|
||||
@@ -587,7 +866,7 @@ function parseModsForList(updated, mods, req) {
|
||||
pkg['dist-tags'] = {
|
||||
latest: pkg.version
|
||||
};
|
||||
common.downloadURL(pkg, req);
|
||||
common.setDownloadURL(pkg, req);
|
||||
results[mod.name] = pkg;
|
||||
}
|
||||
return results;
|
||||
|
||||
@@ -13,13 +13,27 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
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;
|
||||
SyncModuleWorker.sync(name, username, function (err, result) {
|
||||
var publish = req.query.publish === 'true';
|
||||
var noDep = req.query.nodeps === 'true';
|
||||
if (publish && !req.session.isAdmin) {
|
||||
return res.json(403, {
|
||||
error: 'no_perms',
|
||||
reason: 'Only admin can publish'
|
||||
});
|
||||
}
|
||||
|
||||
var options = {
|
||||
publish: publish,
|
||||
noDep: noDep,
|
||||
};
|
||||
SyncModuleWorker.sync(name, username, options, function (err, result) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
@@ -14,16 +14,20 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var giturl = require('giturl');
|
||||
var moment = require('moment');
|
||||
var eventproxy = require('eventproxy');
|
||||
var semver = require('semver');
|
||||
var marked = require('marked');
|
||||
var gravatar = require('gravatar');
|
||||
var humanize = require('humanize-number');
|
||||
var config = require('../../config');
|
||||
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;
|
||||
@@ -45,7 +49,13 @@ exports.display = function (req, res, next) {
|
||||
|
||||
down.total(name, ep.done('download'));
|
||||
|
||||
ep.all('pkg', 'download', function (pkg, download) {
|
||||
ModuleDeps.list(name, ep.done(function (rows) {
|
||||
ep.emit('dependents', rows.map(function (r) {
|
||||
return r.deps;
|
||||
}));
|
||||
}));
|
||||
|
||||
ep.all('pkg', 'download', 'dependents', function (pkg, download, dependents) {
|
||||
if (!pkg || !pkg.package) {
|
||||
return next();
|
||||
}
|
||||
@@ -53,6 +63,10 @@ exports.display = function (req, res, next) {
|
||||
pkg.package.fromNow = moment(pkg.publish_time).fromNow();
|
||||
pkg = pkg.package;
|
||||
pkg.readme = marked(pkg.readme || '');
|
||||
if (!pkg.readme) {
|
||||
pkg.readme = pkg.description || '';
|
||||
}
|
||||
|
||||
if (pkg.maintainers) {
|
||||
for (var i = 0; i < pkg.maintainers.length; i++) {
|
||||
var maintainer = pkg.maintainers[i];
|
||||
@@ -63,19 +77,33 @@ exports.display = function (req, res, next) {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if (config.packagePageContributorSearch || !contributor.url) {
|
||||
contributor.url = '/~' + encodeURIComponent(contributor.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pkg.repository && pkg.repository.url) {
|
||||
pkg.repository.weburl = giturl.parse(pkg.repository.url);
|
||||
}
|
||||
|
||||
setLicense(pkg);
|
||||
|
||||
for (var k in download) {
|
||||
download[k] = humanize(download[k]);
|
||||
}
|
||||
setDownloadURL(pkg, req, config.registryHost);
|
||||
|
||||
pkg.dependents = dependents;
|
||||
|
||||
res.render('package', {
|
||||
title: 'Package - ' + pkg.name,
|
||||
@@ -88,7 +116,7 @@ exports.display = function (req, res, next) {
|
||||
exports.search = function (req, res, next) {
|
||||
var params = req.params;
|
||||
var word = req.params.word;
|
||||
Module.search(word, function (err, packages) {
|
||||
Module.search(word, function (err, result) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
@@ -96,13 +124,49 @@ exports.search = function (req, res, next) {
|
||||
res.render('search', {
|
||||
title: 'Keyword - ' + word,
|
||||
keyword: word,
|
||||
packages: packages || []
|
||||
packages: result.searchMatchs,
|
||||
keywords: result.keywordMatchs,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.rangeSearch = function (req, res, next) {
|
||||
var startKey = req.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, result) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
res.json({
|
||||
rows: rows
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.displaySync = function (req, res, next) {
|
||||
var name = req.params.name;
|
||||
var name = req.params.name || req.query.name;
|
||||
res.render('sync', {
|
||||
name: name,
|
||||
title: 'Sync - ' + name
|
||||
|
||||
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';
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# cnpmjs.org: Private npm registry and web for Enterprise
|
||||
|
||||
[](http://travis-ci.org/fengmk2/cnpmjs.org) [](https://coveralls.io/r/fengmk2/cnpmjs.org) [](https://gemnasium.com/fengmk2/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/)
|
||||
|
||||
## What is this?
|
||||
|
||||
> Private npm registry and web for Enterprise, base on MySQL and Simple File Store.
|
||||
> Private npm registry and web for Enterprise, base on 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).
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
## Registry
|
||||
|
||||
* Our public registry: [registry.cnpmjs.org](http://registry.cnpmjs.org)
|
||||
* Our public registry: [r.cnpmjs.org](http://r.cnpmjs.org), syncing from [registry.npmjs.org](http://registry.npmjs.org)
|
||||
* Current [cnpmjs.org](/) version: <span id="app-version"></span>
|
||||
|
||||
<table class="downloads">
|
||||
@@ -119,13 +119,13 @@ $(function () {
|
||||
alias it:
|
||||
|
||||
```bash
|
||||
alias cnpm="npm --registry=http://registry.cnpmjs.org \
|
||||
alias cnpm="npm --registry=http://r.cnpmjs.org \
|
||||
--cache=$HOME/.npm/.cache/cnpm \
|
||||
--disturl=http://dist.u.qiniudn.com \
|
||||
--userconfig=$HOME/.cnpmrc"
|
||||
|
||||
#Or alias it in .bashrc or .zshrc
|
||||
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=http://registry.cnpmjs.org \
|
||||
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=http://r.cnpmjs.org \
|
||||
--cache=$HOME/.npm/.cache/cnpm \
|
||||
--disturl=http://dist.u.qiniudn.com \
|
||||
--userconfig=$HOME/.cnpmrc"' >> ~/.zshrc && source ~/.zshrc
|
||||
@@ -139,7 +139,7 @@ $ npm install cnpm -g
|
||||
|
||||
### install
|
||||
|
||||
Install package from [registry.cnpmjs.org](http://registry.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 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.
|
||||
|
||||
```
|
||||
$ cnpm install [name]
|
||||
@@ -178,7 +178,7 @@ $ cnpm info cnpm
|
||||
|
||||
## TODO list
|
||||
|
||||
@see Github [Issues](https://github.com/fengmk2/cnpmjs.org/issues)
|
||||
@see Github [Issues](https://github.com/cnpm/cnpmjs.org/issues)
|
||||
|
||||
## Authors
|
||||
|
||||
@@ -188,14 +188,15 @@ Release [History](/history).
|
||||
$ git summary
|
||||
|
||||
project : cnpmjs.org
|
||||
repo age : 2 weeks
|
||||
active : 81 days
|
||||
commits : 205
|
||||
files : 83
|
||||
repo age : 7 weeks
|
||||
active : 132 days
|
||||
commits : 315
|
||||
files : 88
|
||||
authors :
|
||||
120 fengmk2 58.5%
|
||||
84 dead_horse 41.0%
|
||||
1 Alsotang 0.5%
|
||||
190 fengmk2 60.3%
|
||||
122 dead_horse 38.7%
|
||||
2 4simple 0.6%
|
||||
1 Alsotang 0.3%
|
||||
```
|
||||
|
||||
## npm and cnpm relation
|
||||
|
||||
30
index.js
Normal file
30
index.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/*!
|
||||
* cnpmjs.org - index.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var config = require('./config');
|
||||
|
||||
exports.loadConfig = config.loadConfig;
|
||||
exports.config = config;
|
||||
|
||||
exports.startWorker = function (customConfig) {
|
||||
config.loadConfig(customConfig);
|
||||
require('./worker');
|
||||
};
|
||||
|
||||
exports.startSync = function (customConfig) {
|
||||
config.loadConfig(customConfig);
|
||||
require('./sync');
|
||||
};
|
||||
@@ -17,6 +17,7 @@
|
||||
var crypto = require('crypto');
|
||||
var path = require('path');
|
||||
var config = require('../config');
|
||||
var util = require('util');
|
||||
|
||||
exports.getTarballFilepath = function (filename) {
|
||||
// ensure download file path unique
|
||||
@@ -28,8 +29,29 @@ exports.getCDNKey = function (name, filename) {
|
||||
return '/' + name + '/-/' + filename;
|
||||
};
|
||||
|
||||
exports.downloadURL = function (pkg, req) {
|
||||
if (pkg.dist && pkg.dist.tarball) {
|
||||
pkg.dist.tarball = 'http://' + req.headers.host + '/' + pkg.name + '/download/' + path.basename(pkg.dist.tarball);
|
||||
exports.setDownloadURL = function (pkg, req, host) {
|
||||
if (pkg.dist) {
|
||||
host = host || req.headers.host;
|
||||
pkg.dist.tarball = util.format('%s://%s/%s/download/%s-%s.tgz',
|
||||
req.connection.encrypted ? 'https' : 'http',
|
||||
host, pkg.name, pkg.name, pkg.version);
|
||||
}
|
||||
};
|
||||
|
||||
exports.isAdmin = function (username) {
|
||||
return typeof config.admins[username] === 'string';
|
||||
};
|
||||
|
||||
exports.isMaintainer = function (req, maintainers) {
|
||||
if (req.session.isAdmin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var username = req.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.
|
||||
@@ -17,14 +17,18 @@
|
||||
var debug = require('debug')('cnpmjs.org:middleware:auth');
|
||||
var User = require('../proxy/user');
|
||||
var config = require('../config');
|
||||
var common = require('../lib/common');
|
||||
|
||||
module.exports = function (options) {
|
||||
return function auth(req, res, next) {
|
||||
if (!req.session) {
|
||||
// redis crash
|
||||
req.session = {};
|
||||
return next();
|
||||
}
|
||||
req.session.onlySync = config.enablePrivate ? true : false;
|
||||
if (req.session.name) {
|
||||
if (config.admins.hasOwnProperty[req.session.name]) {
|
||||
req.session.isAdmin = true;
|
||||
}
|
||||
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();
|
||||
@@ -34,7 +38,12 @@ module.exports = function (options) {
|
||||
if (!authorization) {
|
||||
return next();
|
||||
}
|
||||
|
||||
authorization = new Buffer(authorization, 'base64').toString().split(':');
|
||||
if (authorization.length !== 2) {
|
||||
return next();
|
||||
}
|
||||
|
||||
var username = authorization[0];
|
||||
var password = authorization[1];
|
||||
|
||||
@@ -45,16 +54,13 @@ module.exports = function (options) {
|
||||
|
||||
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 = null;
|
||||
req.session.isAdmin = false;
|
||||
return next();
|
||||
}
|
||||
|
||||
req.session.name = row.name;
|
||||
if (config.admins.hasOwnProperty(req.session.name)) {
|
||||
req.session.isAdmin = true;
|
||||
}
|
||||
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();
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:middleware:sync_by_install');
|
||||
var config = require('../config');
|
||||
|
||||
/**
|
||||
@@ -36,5 +37,9 @@ module.exports = function (req, res, next) {
|
||||
req.session.allowSync = false;
|
||||
}
|
||||
|
||||
// TODO: allow sync will let publish sync package...
|
||||
req.session.allowSync = false;
|
||||
|
||||
debug('%s allowSync: %s', req.session.name, req.session.allowSync);
|
||||
next();
|
||||
};
|
||||
|
||||
31
package.json
31
package.json
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "cnpmjs.org",
|
||||
"version": "0.2.0",
|
||||
"description": "Private npm registry and web",
|
||||
"main": "dispatch.js",
|
||||
"version": "0.2.26",
|
||||
"description": "Private npm registry and web for Enterprise, base on MySQL and Simple Store Service",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "make test-all",
|
||||
"start": "./bin/nodejsctl start && cp History.md docs/web/history.md",
|
||||
@@ -30,17 +30,19 @@
|
||||
"debug": "0.7.4",
|
||||
"eventproxy": "0.2.6",
|
||||
"forward": "0.0.4",
|
||||
"giturl": "0.0.1",
|
||||
"graceful": "0.0.5",
|
||||
"gravatar": "1.0.6",
|
||||
"humanize-number": "0.0.2",
|
||||
"logfilestream": "0.1.0",
|
||||
"marked": "0.3.0",
|
||||
"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-rc2",
|
||||
"nodemailer": "0.5.15",
|
||||
"mysql": "2.0.1",
|
||||
"nodemailer": "0.6.0",
|
||||
"qn": "0.2.0",
|
||||
"ready": "0.1.1",
|
||||
"response-cookie": "0.0.2",
|
||||
@@ -48,28 +50,29 @@
|
||||
"semver": "2.2.1",
|
||||
"urllib": "0.5.5",
|
||||
"urlrouter": "0.5.4",
|
||||
"utility": "0.1.9"
|
||||
"utility": "0.1.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autod": ">=0.0.13",
|
||||
"blanket": "*",
|
||||
"contributors": "*",
|
||||
"coveralls": "*",
|
||||
"mm": "0.1.7",
|
||||
"mm": "0.1.8",
|
||||
"mocha": "*",
|
||||
"mocha-lcov-reporter": "*",
|
||||
"pedding": "0.0.3",
|
||||
"should": "2.1.1",
|
||||
"supertest": "0.8.2",
|
||||
"should": "3.0.1",
|
||||
"supertest": "0.9.0",
|
||||
"travis-cov": "*"
|
||||
},
|
||||
"homepage": "https://github.com/fengmk2/cnpmjs.org",
|
||||
"homepage": "https://github.com/cnpm/cnpmjs.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/fengmk2/cnpmjs.org.git",
|
||||
"web": "https://github.com/fengmk2/cnpmjs.org"
|
||||
"url": "git://github.com/cnpm/cnpmjs.org.git",
|
||||
"web": "https://github.com/cnpm/cnpmjs.org"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/fengmk2/cnpmjs.org/issues",
|
||||
"url": "https://github.com/cnpm/cnpmjs.org/issues",
|
||||
"email": "fengmk2@gmail.com"
|
||||
},
|
||||
"keywords": [
|
||||
|
||||
167
proxy/module.js
167
proxy/module.js
@@ -29,6 +29,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 +52,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 +128,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 +152,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 +175,7 @@ function parseRow(row) {
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.parseRow = parseRow;
|
||||
|
||||
function stringifyPackage(pkg) {
|
||||
return encodeURIComponent(JSON.stringify(pkg));
|
||||
@@ -171,7 +256,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,26 +385,67 @@ exports.listByAuthor = function (author, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var SEARCH_SQLS = [
|
||||
'SELECT module_id FROM tag WHERE name LIKE ? AND tag="latest" ORDER BY name LIMIT 100;',
|
||||
'SELECT name, description FROM module WHERE id IN (?) ORDER BY name;'
|
||||
];
|
||||
exports.search = function (word, callback) {
|
||||
word = word.replace(/^%/, '') + '%'; //ignore prefix %
|
||||
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;
|
||||
options = null;
|
||||
}
|
||||
options = options || {};
|
||||
var limit = options.limit || 100;
|
||||
word = word.replace(/^%/, ''); //ignore prefix %
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(callback);
|
||||
mysql.query(SEARCH_SQLS[0], [word], 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);
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
41
proxy/module_deps.js
Normal file
41
proxy/module_deps.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/**!
|
||||
* 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 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);
|
||||
};
|
||||
17
proxy/npm.js
17
proxy/npm.js
@@ -20,15 +20,22 @@ var config = require('../config');
|
||||
function request(url, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {
|
||||
dataType: 'json',
|
||||
timeout: 10000
|
||||
};
|
||||
options = null;
|
||||
}
|
||||
options = options || {};
|
||||
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) {
|
||||
|
||||
@@ -24,12 +24,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);
|
||||
@@ -47,6 +49,8 @@ function SyncModuleWorker(options) {
|
||||
|
||||
this.username = options.username;
|
||||
this.concurrency = options.concurrency || 1;
|
||||
this._publish = options.publish; // _publish_on_cnpm
|
||||
|
||||
this.syncingNames = {};
|
||||
this.nameMap = {};
|
||||
this.names.forEach(function (name) {
|
||||
@@ -82,7 +86,8 @@ SyncModuleWorker.prototype.log = function (format, arg1, arg2) {
|
||||
};
|
||||
|
||||
SyncModuleWorker.prototype.start = function () {
|
||||
this.log('user: %s, sync %s worker start, %d concurrency.', this.username, this.names[0], this.concurrency);
|
||||
this.log('user: %s, sync %s worker start, %d concurrency, nodeps: %s, publish: %s',
|
||||
this.username, this.names[0], this.concurrency, this.noDep, this._publish);
|
||||
for (var i = 0; i < this.concurrency; i++) {
|
||||
this.next(i);
|
||||
}
|
||||
@@ -122,11 +127,11 @@ SyncModuleWorker.prototype.next = function (concurrencyId) {
|
||||
return that.next(concurrencyId);
|
||||
}
|
||||
|
||||
that.log('[c#%d] [%s] start...', concurrencyId, pkg.name);
|
||||
that._sync(pkg, function (err, versions) {
|
||||
that.log('[c#%d] [%s] start...', concurrencyId, name);
|
||||
that._sync(name, pkg, function (err, versions) {
|
||||
delete that.syncingNames[name];
|
||||
if (err) {
|
||||
that.fails.push(pkg.name);
|
||||
that.fails.push(name);
|
||||
that.log('[error] [%s] sync error: %s', name, err.stack);
|
||||
return that.next(concurrencyId);
|
||||
}
|
||||
@@ -139,9 +144,8 @@ SyncModuleWorker.prototype.next = function (concurrencyId) {
|
||||
});
|
||||
};
|
||||
|
||||
SyncModuleWorker.prototype._sync = function (pkg, callback) {
|
||||
var username = this.name;
|
||||
var name = pkg.name;
|
||||
SyncModuleWorker.prototype._sync = function (name, pkg, callback) {
|
||||
var username = this.username;
|
||||
var that = this;
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(callback);
|
||||
@@ -159,7 +163,7 @@ SyncModuleWorker.prototype._sync = function (pkg, callback) {
|
||||
|
||||
if (r.package && r.package._publish_on_cnpm) {
|
||||
// publish on cnpm, dont sync this version package
|
||||
that.log(' [%s] publish on local cnpm, don\'t sync.', pkg.name);
|
||||
that.log(' [%s] publish on local cnpm, don\'t sync', name);
|
||||
ep.unbind();
|
||||
callback(null, []);
|
||||
return;
|
||||
@@ -192,6 +196,8 @@ SyncModuleWorker.prototype._sync = function (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 || {};
|
||||
@@ -200,7 +206,7 @@ SyncModuleWorker.prototype._sync = function (pkg, callback) {
|
||||
versionNames = Object.keys(pkg.versions);
|
||||
}
|
||||
if (versionNames.length === 0) {
|
||||
that.log(' [%s] no times and no versions, hasModules: %s', pkg.name, hasModules);
|
||||
that.log(' [%s] no times and no versions, hasModules: %s', name, hasModules);
|
||||
if (!hasModules) {
|
||||
// save a next module
|
||||
var maintainer = pkg.maintainers && pkg.maintainers[0];
|
||||
@@ -211,11 +217,11 @@ SyncModuleWorker.prototype._sync = function (pkg, callback) {
|
||||
maintainer = '-';
|
||||
}
|
||||
var nextMod = {
|
||||
name: pkg.name,
|
||||
name: name,
|
||||
version: 'next',
|
||||
author: maintainer,
|
||||
package: {
|
||||
name: pkg.name,
|
||||
name: name,
|
||||
version: 'next',
|
||||
description: pkg.description || '',
|
||||
readme: pkg.readme || '',
|
||||
@@ -225,7 +231,7 @@ SyncModuleWorker.prototype._sync = function (pkg, callback) {
|
||||
},
|
||||
};
|
||||
Module.add(nextMod, function (err, result) {
|
||||
that.log(' [%s] save next module, %j, error: %s', pkg.name, result, err);
|
||||
that.log(' [%s] save next module, %j, error: %s', name, result, err);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -237,7 +243,10 @@ SyncModuleWorker.prototype._sync = function (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]) {
|
||||
@@ -250,7 +259,7 @@ SyncModuleWorker.prototype._sync = function (pkg, callback) {
|
||||
// * shasum make sure equal
|
||||
if ((version.publish_time === exists.publish_time) || (!version.publish_time && exists.publish_time)) {
|
||||
debug(' [%s] %s publish_time equal: %s, %s',
|
||||
pkg.name, version.version, version.publish_time, exists.publish_time);
|
||||
name, version.version, version.publish_time, exists.publish_time);
|
||||
// * publish_time make sure equal
|
||||
if (exists.description === null && version.description) {
|
||||
// * make sure description exists
|
||||
@@ -259,6 +268,14 @@ SyncModuleWorker.prototype._sync = function (pkg, callback) {
|
||||
description: version.description
|
||||
});
|
||||
}
|
||||
|
||||
if (!exists.package.readme && version.readme) {
|
||||
// * make sure readme exists
|
||||
missingReadmes.push({
|
||||
id: exists.id,
|
||||
readme: version.readme
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -274,7 +291,7 @@ SyncModuleWorker.prototype._sync = function (pkg, callback) {
|
||||
}
|
||||
|
||||
if (versions.length === 0) {
|
||||
that.log(' [%s] all versions are exists', pkg.name);
|
||||
that.log(' [%s] all versions are exists', name);
|
||||
return ep.emit('syncDone');
|
||||
}
|
||||
|
||||
@@ -282,7 +299,7 @@ SyncModuleWorker.prototype._sync = function (pkg, callback) {
|
||||
return a.publish_time - b.publish_time;
|
||||
});
|
||||
missingVersions = versions;
|
||||
that.log(' [%s] %d versions', pkg.name, versions.length);
|
||||
that.log(' [%s] %d versions', name, versions.length);
|
||||
ep.emit('syncModule', missingVersions.shift());
|
||||
});
|
||||
|
||||
@@ -311,14 +328,15 @@ SyncModuleWorker.prototype._sync = function (pkg, callback) {
|
||||
return ep.emit('descriptionDone');
|
||||
}
|
||||
|
||||
that.log(' [%s] saving %d descriptions', pkg.name, missingDescriptions.length);
|
||||
that.log(' [%s] saving %d descriptions', name, missingDescriptions.length);
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -332,12 +350,12 @@ SyncModuleWorker.prototype._sync = function (pkg, callback) {
|
||||
return ep.emit('tagDone');
|
||||
}
|
||||
|
||||
that.log(' [%s] adding %d tags', pkg.name, missingTags.length);
|
||||
that.log(' [%s] adding %d tags', name, missingTags.length);
|
||||
// sync tags
|
||||
missingTags.forEach(function (item) {
|
||||
Module.addTag(pkg.name, item[0], item[1], ep.done(function (result) {
|
||||
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');
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -346,7 +364,29 @@ SyncModuleWorker.prototype._sync = function (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);
|
||||
});
|
||||
@@ -371,18 +411,37 @@ SyncModuleWorker.prototype._syncOneVersion = function (versionIndex, sourcePacka
|
||||
callback(err);
|
||||
});
|
||||
|
||||
that.log(' [%s:%d] syncing, version: %s, dist: %j',
|
||||
sourcePackage.name, versionIndex, sourcePackage.version, sourcePackage.dist);
|
||||
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,
|
||||
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) {
|
||||
@@ -419,8 +478,12 @@ SyncModuleWorker.prototype._syncOneVersion = function (versionIndex, sourcePacka
|
||||
return ep.emit('error', err);
|
||||
}
|
||||
|
||||
var key = common.getCDNKey(sourcePackage.name, filename);
|
||||
nfs.upload(filepath, {key: key, size: dataSize}, ep.done('uploadResult'));
|
||||
var options = {
|
||||
key: common.getCDNKey(sourcePackage.name, filename),
|
||||
size: dataSize,
|
||||
shasum: shasum
|
||||
};
|
||||
nfs.upload(filepath, options, ep.done('uploadResult'));
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -442,24 +505,44 @@ SyncModuleWorker.prototype._syncOneVersion = function (versionIndex, sourcePacka
|
||||
author: author,
|
||||
publish_time: sourcePackage.publish_time,
|
||||
};
|
||||
|
||||
if (that._publish) {
|
||||
// sync as publish
|
||||
mod.package._publish_on_cnpm = true;
|
||||
}
|
||||
|
||||
var dist = {
|
||||
tarball: result.url,
|
||||
shasum: shasum,
|
||||
size: dataSize,
|
||||
noattachment: dataSize === 0,
|
||||
};
|
||||
|
||||
if (result.url) {
|
||||
dist.tarball = result.url;
|
||||
} else if (result.key) {
|
||||
dist.key = result.key;
|
||||
dist.tarball = result.key;
|
||||
}
|
||||
|
||||
mod.package.dist = dist;
|
||||
Module.add(mod, ep.done(function (result) {
|
||||
that.log(' [%s:%s] done, insertId: %s, author: %s, version: %s, size: %d, publish_time: %j',
|
||||
that.log(' [%s:%s] done, insertId: %s, author: %s, version: %s, size: %d, publish_time: %j, publish on cnpm: %s',
|
||||
sourcePackage.name, versionIndex,
|
||||
result.id,
|
||||
author, mod.version, dataSize, new Date(mod.publish_time));
|
||||
author, mod.version, dataSize,
|
||||
new Date(mod.publish_time),
|
||||
that._publish);
|
||||
callback(null, result);
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
SyncModuleWorker.sync = function (name, username, callback) {
|
||||
SyncModuleWorker.sync = function (name, username, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
options = options || {};
|
||||
npm.get(name, function (err, pkg, response) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
@@ -479,6 +562,8 @@ SyncModuleWorker.sync = function (name, username, callback) {
|
||||
logId: result.id,
|
||||
name: name,
|
||||
username: username,
|
||||
noDep: options.noDep,
|
||||
publish: options.publish,
|
||||
});
|
||||
worker.start();
|
||||
callback(null, {
|
||||
|
||||
@@ -62,7 +62,7 @@ function routes(app) {
|
||||
// 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);
|
||||
app.put('/-/user/org.couchdb.user::name/-rev/:rev', [login], user.update);
|
||||
|
||||
// _session
|
||||
app.post('/_session', user.authSession);
|
||||
|
||||
@@ -31,6 +31,9 @@ function routes(app) {
|
||||
app.get('/sync/:name', pkg.displaySync);
|
||||
app.put('/sync/:name', sync.sync);
|
||||
app.get('/sync/:name/log/:id', sync.getSyncLog);
|
||||
app.get('/sync', pkg.displaySync);
|
||||
|
||||
app.get('/_list/search/search', pkg.rangeSearch);
|
||||
}
|
||||
|
||||
module.exports = routes;
|
||||
|
||||
@@ -65,9 +65,9 @@ app.use(function (req, res, next) {
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
err.url = err.url || req.url;
|
||||
logger.error(err);
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
console.error(err.stack);
|
||||
logger.error(err);
|
||||
}
|
||||
if (config.debug) {
|
||||
return next(err);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
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');
|
||||
@@ -49,9 +50,16 @@ app.use(connect.json());
|
||||
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)
|
||||
.replace('{{logoURL}}', config.logoURL);
|
||||
fs.writeFileSync(layoutFile, layout);
|
||||
|
||||
app.use('/', connectMarkdown({
|
||||
root: docDir,
|
||||
layout: path.join(viewDir, 'layout.html'),
|
||||
layout: layoutFile,
|
||||
titleHolder: '<%- locals.title %>',
|
||||
bodyHolder: '<%- locals.body %>',
|
||||
indexName: 'readme'
|
||||
@@ -64,7 +72,7 @@ var helpers = {
|
||||
app.use(render({
|
||||
root: viewDir,
|
||||
viewExt: '.html',
|
||||
layout: 'layout',
|
||||
layout: '_layout',
|
||||
cache: config.viewCache,
|
||||
helpers: helpers
|
||||
}));
|
||||
@@ -81,8 +89,10 @@ app.use(urlrouter(routes));
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
err.url = err.url || req.url;
|
||||
console.error(err.stack);
|
||||
logger.error(err);
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
console.error(err.stack);
|
||||
}
|
||||
if (config.debug) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,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');
|
||||
|
||||
@@ -88,14 +89,17 @@ describe('controllers/registry/module.test.js', function () {
|
||||
describe('GET /:name/:(version|tag)', function () {
|
||||
it('should return module@version info', function (done) {
|
||||
request(app)
|
||||
.get('/cnpmjs.org/0.0.0')
|
||||
.get('/cnpmjs.org/0.2.1')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var body = res.body;
|
||||
body.name.should.equal('cnpmjs.org');
|
||||
body.version.should.equal('0.0.0');
|
||||
body._id.should.equal('cnpmjs.org@0.0.0');
|
||||
body.dist.tarball.should.include('cnpmjs.org-0.0.0.tgz');
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -114,14 +118,14 @@ describe('controllers/registry/module.test.js', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should get cnpmjs.org@0.1.0 with _publish_on_cnpm=true', function (done) {
|
||||
it('should get cnpmjs.org@0.2.1 with _publish_on_cnpm=true', function (done) {
|
||||
request(app)
|
||||
.get('/cnpmjs.org/0.1.0')
|
||||
.get('/cnpmjs.org/0.2.1')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var body = res.body;
|
||||
body.name.should.equal('cnpmjs.org');
|
||||
body.version.should.equal('0.1.0');
|
||||
body.version.should.equal('0.2.1');
|
||||
body._publish_on_cnpm.should.equal(true);
|
||||
done();
|
||||
});
|
||||
@@ -137,6 +141,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');
|
||||
@@ -172,14 +183,7 @@ describe('controllers/registry/module.test.js', function () {
|
||||
.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();
|
||||
});
|
||||
.expect(201, done);
|
||||
});
|
||||
|
||||
it('should try to add return 403 when not module user and only next module exists',
|
||||
@@ -291,6 +295,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)
|
||||
@@ -298,7 +303,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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -442,6 +456,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,6 +468,43 @@ describe('controllers/registry/module.test.js', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /:name/download/:filename', function () {
|
||||
it('should download a file with 302 redirect', function (done) {
|
||||
request(app)
|
||||
.get('/cutter/download/cutter-0.0.2.tgz')
|
||||
.expect('Location', 'http://qtestbucket.qiniudn.com/cutter/-/cutter-0.0.2.tgz')
|
||||
.expect(302, done)
|
||||
});
|
||||
|
||||
it('should download a file direct from nfs stream', function (done) {
|
||||
var nfs = require('../../../common/nfs');
|
||||
mm(nfs, 'downloadStream', 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 () {
|
||||
var lastRev;
|
||||
before(function (done) {
|
||||
|
||||
@@ -40,6 +40,54 @@ describe('controllers/sync.test.js', function () {
|
||||
describe('sync source npm package', function () {
|
||||
var logIdRegistry;
|
||||
var logIdWeb;
|
||||
|
||||
it('should sync as publish success', function (done) {
|
||||
request(registryApp)
|
||||
.del('/utility/-rev/123')
|
||||
.set('authorization', baseauth)
|
||||
// .expect(200)
|
||||
// .expect({ok: true})
|
||||
.end(function (err, res) {
|
||||
should.not.exist(err);
|
||||
|
||||
mm.data(Npm, 'get', require(path.join(fixtures, 'utility.json')));
|
||||
request(registryApp)
|
||||
.put('/utility/sync?publish=true&nodeps=true')
|
||||
.set('authorization', baseauth)
|
||||
.end(function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'logId');
|
||||
logIdRegistry = res.body.logId;
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should sync as publish 403 when user not admin', function (done) {
|
||||
mm.data(Npm, 'get', require(path.join(fixtures, 'utility.json')));
|
||||
request(registryApp)
|
||||
.put('/utility_unit_test/sync?publish=true&nodeps=true')
|
||||
.expect(403)
|
||||
.expect({
|
||||
error: 'no_perms',
|
||||
reason: 'Only admin can publish'
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should sync success', function (done) {
|
||||
mm.data(Npm, 'get', require(path.join(fixtures, 'utility.json')));
|
||||
done = pedding(2, done);
|
||||
|
||||
@@ -31,6 +31,48 @@ describe('controllers/web/package.test.js', function () {
|
||||
|
||||
afterEach(mm.restore);
|
||||
|
||||
describe('GET /_list/search/search', function () {
|
||||
it('should search with "c"', function (done) {
|
||||
request(app)
|
||||
.get('/_list/search/search?startkey="c"&limit=2')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('rows');
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
it('should search with c', function (done) {
|
||||
request(app)
|
||||
.get('/_list/search/search?startkey=c&limit=2')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('rows');
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
it('should search return empty', function (done) {
|
||||
request(app)
|
||||
.get('/_list/search/search?startkey="cddddsdasdaasds"&limit=2')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.eql({rows: []});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /package/:name', function (done) {
|
||||
it('should get 200', function (done) {
|
||||
request(app)
|
||||
|
||||
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
|
||||
29
test/lib/common.test.js
Normal file
29
test/lib/common.test.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/*!
|
||||
* cnpmjs.org - test/lib/common.test.js.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com>
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var common = require('../../lib/common');
|
||||
|
||||
describe('lib/common.test.js', function () {
|
||||
describe('isAdmin()', function () {
|
||||
it('should admin is admin', function () {
|
||||
common.isAdmin('admin').should.equal(true);
|
||||
common.isAdmin('fengmk2').should.equal(true);
|
||||
common.isAdmin('constructor').should.equal(false);
|
||||
common.isAdmin('toString').should.equal(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -29,8 +29,9 @@ describe('middleware/auth.test.js', function () {
|
||||
app.close(done);
|
||||
});
|
||||
|
||||
afterEach(mm.restore);
|
||||
|
||||
describe('auth()', function () {
|
||||
afterEach(mm.restore);
|
||||
it('should pass if no authorization', function (done) {
|
||||
request(app)
|
||||
.get('/-/user/org.couchdb.user:cnpmjstest1')
|
||||
@@ -44,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) {
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -30,6 +30,8 @@ Log.create({
|
||||
name: names,
|
||||
username: 'fengmk2',
|
||||
concurrency: names.length,
|
||||
noDep: true,
|
||||
publish: true,
|
||||
});
|
||||
|
||||
worker.start();
|
||||
|
||||
@@ -44,7 +44,7 @@ describe('sync/sync_exist.js', function () {
|
||||
should.not.exist(err);
|
||||
data.successes.should.eql(['cnpmjs.org', 'cutter']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
5
view/web/footer.html
Normal file
5
view/web/footer.html
Normal file
@@ -0,0 +1,5 @@
|
||||
Copyright 2013 - 2014 © cnpmjs.org
|
||||
|
|
||||
<a href="/">Home</a>
|
||||
|
|
||||
<script>var cnzz_protocol = (("https:" == document.location.protocol) ? " https://" : " http://");document.write(unescape("%3Cspan id='cnzz_stat_icon_5757157'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "s17.cnzz.com/stat.php%3Fid%3D5757157%26online%3D1%26show%3Dline' type='text/javascript'%3E%3C/script%3E"));</script>
|
||||
@@ -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,26 +45,32 @@
|
||||
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>
|
||||
<div class="bottom">
|
||||
<hr/>
|
||||
<p>
|
||||
Copyright 2013 - 2014 © cnpmjs.org
|
||||
|
|
||||
<a href="/">Home</a>
|
||||
|
|
||||
<script>var cnzz_protocol = (("https:" == document.location.protocol) ? " https://" : " http://");document.write(unescape("%3Cspan id='cnzz_stat_icon_5757157'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "s17.cnzz.com/stat.php%3Fid%3D5757157%26online%3D1%26show%3Dline' type='text/javascript'%3E%3C/script%3E"));</script>
|
||||
{{footer}}
|
||||
</p>
|
||||
<a href="https://github.com/fengmk2/cnpmjs.org" id="fork" target="_blank">
|
||||
<a href="https://github.com/cnpm/cnpmjs.org" id="fork" target="_blank">
|
||||
<img alt="Fork me on GitHub" src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
<div id="package">
|
||||
<h1>
|
||||
<%= package.name %>
|
||||
<small>(<a href="/sync/<%= package.name %>" target="_blank">SYNC</a> missed versions from official npm registry)</small>
|
||||
<small>
|
||||
<% if (package._publish_on_cnpm) { %>
|
||||
(Private package)
|
||||
<% } else { %>
|
||||
(<a href="/sync/<%= package.name %>" target="_blank">SYNC</a> missed versions from official npm registry)
|
||||
<% } %>
|
||||
</small>
|
||||
</h1>
|
||||
|
||||
<% if (package.deprecated) { %>
|
||||
@@ -12,193 +18,231 @@
|
||||
<p class="description"><%= package.description %></p>
|
||||
<% } %>
|
||||
|
||||
<pre class="sh"><code>$ cnpm 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 (package.maintainers) { %>
|
||||
<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 (package.contributors) { %>
|
||||
<tr>
|
||||
<th>Contributors</th>
|
||||
<td>
|
||||
<% package.contributors.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>
|
||||
<% } %>
|
||||
</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>
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
var $notify = $('#sync-notify');
|
||||
var timer;
|
||||
var name = '<%= name %>';
|
||||
var resourceURL = '/sync/' + name;
|
||||
$(function() {
|
||||
var checkLogId = location.hash.match(/logid=(\d+)/);
|
||||
var logid = checkLogId ? checkLogId[1] : '';
|
||||
@@ -18,7 +19,7 @@
|
||||
return getSyncLog(logid);
|
||||
}
|
||||
$.ajax({
|
||||
url: location.pathname,
|
||||
url: resourceURL,
|
||||
type: 'PUT',
|
||||
dataType: 'json',
|
||||
success: handleSyncSucess,
|
||||
@@ -53,7 +54,7 @@
|
||||
var hasFail = false;
|
||||
function getSyncLog(id) {
|
||||
$.ajax({
|
||||
url: location.pathname + '/log/' + id + '?offset=' + offset,
|
||||
url: resourceURL + '/log/' + id + '?offset=' + offset,
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
|
||||
Reference in New Issue
Block a user