Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -19,3 +19,4 @@ config/config.js
|
||||
backup/*.json
|
||||
backup/*.gz
|
||||
docs/web/history.md
|
||||
view/web/_layout.html
|
||||
|
||||
@@ -8,3 +8,4 @@ logo.png
|
||||
public/dist/
|
||||
backup/*.json
|
||||
backup/*.gz
|
||||
view/web/_layout.html
|
||||
|
||||
56
History.md
56
History.md
@@ -1,4 +1,60 @@
|
||||
|
||||
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
|
||||
==================
|
||||
|
||||
|
||||
@@ -9,12 +9,13 @@ 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
|
||||
|
||||
@@ -19,6 +19,8 @@ var config = require('../config');
|
||||
|
||||
var client = qn.create(config.qn);
|
||||
|
||||
exports._client = client;
|
||||
|
||||
/**
|
||||
* Upload file
|
||||
*
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -71,6 +71,10 @@ var config = {
|
||||
debug: false
|
||||
},
|
||||
|
||||
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: {
|
||||
|
||||
@@ -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,6 @@ 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');
|
||||
|
||||
exports.show = function (req, res, next) {
|
||||
var name = req.params.name;
|
||||
@@ -167,6 +169,8 @@ 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;
|
||||
@@ -181,9 +185,9 @@ exports.download = function (req, res, next) {
|
||||
}
|
||||
var dist = row.package.dist;
|
||||
if (dist.key) {
|
||||
return ep.emit('key', dist.key);
|
||||
return ep.emit('key', dist);
|
||||
} else {
|
||||
return ep.emit('url', dist.tarball);
|
||||
return ep.emit('url', dist);
|
||||
}
|
||||
ep.emit('nodist');
|
||||
});
|
||||
@@ -195,23 +199,46 @@ exports.download = function (req, res, next) {
|
||||
ep.emit('url', nfs.url(common.getCDNKey(name, filename)));
|
||||
});
|
||||
|
||||
ep.once('url', function (url) {
|
||||
ep.once('url', function (dist) {
|
||||
res.statusCode = 302;
|
||||
res.setHeader('Location', url);
|
||||
res.setHeader('Location', dist.tarball);
|
||||
res.end();
|
||||
_downloads[name] = (_downloads[name] || 0) + 1;
|
||||
});
|
||||
|
||||
ep.once('key', function (key) {
|
||||
if (!nfs.download) {
|
||||
ep.once('key', function (dist) {
|
||||
if (!nfs.downloadStream && !nfs.download) {
|
||||
return next();
|
||||
}
|
||||
var tmpPath = path.join(config.uploadDir, utility.randomString() + key);
|
||||
|
||||
_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(key, tmpPath, function (err) {
|
||||
nfs.download(dist.key, tmpPath, {timeout: DOWNLOAD_TIMEOUT},
|
||||
function (err) {
|
||||
if (err) {
|
||||
cleanup();
|
||||
return next(err);
|
||||
@@ -220,7 +247,6 @@ exports.download = function (req, res, next) {
|
||||
tarball.on('error', cleanup);
|
||||
tarball.on('end', cleanup);
|
||||
tarball.pipe(res);
|
||||
_downloads[name] = (_downloads[name] || 0) + 1;
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -323,8 +349,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) {
|
||||
@@ -492,9 +523,12 @@ exports.addPackageAndDist = function (req, res, next) {
|
||||
shasum = crypto.createHash('sha1');
|
||||
shasum.update(tarballBuffer);
|
||||
shasum = shasum.digest('hex');
|
||||
var key = common.getCDNKey(name, filename);
|
||||
|
||||
nfs.uploadBuffer(tarballBuffer, {key: key}, ep.done('upload'));
|
||||
var options = {
|
||||
key: common.getCDNKey(name, filename),
|
||||
shasum: shasum
|
||||
};
|
||||
nfs.uploadBuffer(tarballBuffer, options, ep.done('upload'));
|
||||
});
|
||||
|
||||
ep.on('upload', function (result) {
|
||||
|
||||
@@ -20,10 +20,12 @@ 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 setDownloadURL = require('../../lib/common').setDownloadURL;
|
||||
|
||||
exports.display = function (req, res, next) {
|
||||
var params = req.params;
|
||||
@@ -72,6 +74,9 @@ exports.display = function (req, res, next) {
|
||||
if (contributor.email) {
|
||||
contributor.gravatar = gravatar.url(contributor.email, {s: '50', d: 'retro'}, false);
|
||||
}
|
||||
if (config.packagePageContributorSearch || !contributor.url) {
|
||||
contributor.url = '/~' + encodeURIComponent(contributor.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +86,8 @@ exports.display = function (req, res, next) {
|
||||
download[k] = humanize(download[k]);
|
||||
}
|
||||
|
||||
setDownloadURL(pkg, req, config.registryHost);
|
||||
|
||||
res.render('package', {
|
||||
title: 'Package - ' + pkg.name,
|
||||
package: pkg,
|
||||
@@ -105,8 +112,41 @@ exports.search = function (req, res, next) {
|
||||
});
|
||||
};
|
||||
|
||||
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, packages) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
60
dispatch.js
60
dispatch.js
@@ -24,46 +24,36 @@ var workerPath = path.join(__dirname, 'worker.js');
|
||||
var childProcess = require('child_process');
|
||||
var syncPath = path.join(__dirname, 'sync');
|
||||
|
||||
function start(customConfig) {
|
||||
config.loadConfig(customConfig);
|
||||
if (config.enableCluster) {
|
||||
cluster.setupMaster({
|
||||
exec: workerPath
|
||||
});
|
||||
|
||||
if (config.enableCluster) {
|
||||
cluster.setupMaster({
|
||||
exec: workerPath
|
||||
});
|
||||
cluster.on('fork', function (worker) {
|
||||
console.log('[%s] [worker:%d] new worker start', new Date(), worker.process.pid);
|
||||
});
|
||||
|
||||
cluster.on('fork', function (worker) {
|
||||
console.log('[%s] [worker:%d] new worker start', new Date(), worker.process.pid);
|
||||
});
|
||||
cluster.on('disconnect', function (worker) {
|
||||
var w = cluster.fork();
|
||||
console.error('[%s] [master:%s] wroker:%s disconnect! new worker:%s fork',
|
||||
new Date(), process.pid, worker.process.pid, w.process.pid);
|
||||
});
|
||||
|
||||
cluster.on('disconnect', function (worker) {
|
||||
var w = cluster.fork();
|
||||
console.error('[%s] [master:%s] wroker:%s disconnect! new worker:%s fork',
|
||||
new Date(), process.pid, worker.process.pid, w.process.pid);
|
||||
});
|
||||
cluster.on('exit', function (worker, code, signal) {
|
||||
var exitCode = worker.process.exitCode;
|
||||
var err = new Error(util.format('worker %s died (code: %s, signal: %s)', worker.process.pid, exitCode, signal));
|
||||
err.name = 'WorkerDiedError';
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
cluster.on('exit', function (worker, code, signal) {
|
||||
var exitCode = worker.process.exitCode;
|
||||
var err = new Error(util.format('worker %s died (code: %s, signal: %s)', worker.process.pid, exitCode, signal));
|
||||
err.name = 'WorkerDiedError';
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
var numCPUs = require('os').cpus().length;
|
||||
// Fork workers.
|
||||
for (var i = 0; i < numCPUs; i++) {
|
||||
cluster.fork();
|
||||
}
|
||||
|
||||
childProcess.fork(syncPath);
|
||||
} else {
|
||||
require(workerPath);
|
||||
require(syncPath);
|
||||
var numCPUs = require('os').cpus().length;
|
||||
// Fork workers.
|
||||
for (var i = 0; i < numCPUs; i++) {
|
||||
cluster.fork();
|
||||
}
|
||||
}
|
||||
|
||||
if (!module.parent) {
|
||||
start();
|
||||
childProcess.fork(syncPath);
|
||||
} else {
|
||||
module.exports = start;
|
||||
require(workerPath);
|
||||
require(syncPath);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## 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]
|
||||
|
||||
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');
|
||||
};
|
||||
@@ -29,11 +29,12 @@ exports.getCDNKey = function (name, filename) {
|
||||
return '/' + name + '/-/' + filename;
|
||||
};
|
||||
|
||||
exports.setDownloadURL = function (pkg, req) {
|
||||
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',
|
||||
req.headers.host, pkg.name, pkg.name, pkg.version);
|
||||
host, pkg.name, pkg.name, pkg.version);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "cnpmjs.org",
|
||||
"version": "0.2.3",
|
||||
"description": "Private npm registry and web",
|
||||
"main": "dispatch.js",
|
||||
"version": "0.2.13",
|
||||
"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",
|
||||
@@ -36,6 +36,7 @@
|
||||
"logfilestream": "0.1.0",
|
||||
"marked": "0.3.0",
|
||||
"microtime": "0.5.1",
|
||||
"mime": "1.2.11",
|
||||
"mkdirp": "0.3.5",
|
||||
"moment": "2.5.0",
|
||||
"ms": "0.6.2",
|
||||
|
||||
@@ -296,14 +296,20 @@ 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 module_id FROM tag WHERE name LIKE ? AND tag="latest" ORDER BY name LIMIT ?;',
|
||||
'SELECT name, description FROM module WHERE id IN (?) ORDER BY name;'
|
||||
];
|
||||
exports.search = function (word, callback) {
|
||||
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) {
|
||||
mysql.query(SEARCH_SQLS[0], [word, limit], ep.done(function (rows) {
|
||||
if (!rows || rows.length === 0) {
|
||||
return callback(null, []);
|
||||
}
|
||||
|
||||
@@ -20,11 +20,9 @@ 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;
|
||||
|
||||
@@ -422,8 +422,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'));
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
@@ -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,14 @@ 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);
|
||||
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'
|
||||
@@ -81,8 +87,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);
|
||||
}
|
||||
|
||||
@@ -446,6 +446,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) {
|
||||
|
||||
@@ -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.should.eql([
|
||||
{ key: 'c', count: 1, value: { name: 'c', description: 'Give folders or directories comments and view them easy.' } },
|
||||
{ key: 'charset', count: 1,
|
||||
value: { name: 'charset', description: 'Get the content charset from header and html content-type.' } }
|
||||
]);
|
||||
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.should.eql([
|
||||
{ key: 'c', count: 1, value: { name: 'c', description: 'Give folders or directories comments and view them easy.' } },
|
||||
{ key: 'charset', count: 1,
|
||||
value: { name: 'charset', description: 'Get the content charset from header and html content-type.' } }
|
||||
]);
|
||||
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)
|
||||
|
||||
@@ -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>
|
||||
@@ -54,13 +54,9 @@
|
||||
<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>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<p class="description"><%= package.description %></p>
|
||||
<% } %>
|
||||
|
||||
<pre class="sh"><code>$ cnpm install <%= package.name %></code></pre>
|
||||
<pre class="sh"><code>$ <%- config.npmClientName %> install <%= package.name %></code></pre>
|
||||
|
||||
<table class="downloads">
|
||||
<tbody>
|
||||
@@ -181,7 +181,7 @@
|
||||
<td>
|
||||
<% package.contributors.forEach(function (m) { %>
|
||||
<span class="user">
|
||||
<a class="username" href="/~<%= m.name %>">
|
||||
<a class="username" target="_blank" href="<%= m.url %>">
|
||||
<% if (m.gravatar) { %>
|
||||
<img src="<%- m.gravatar %>" class="avatar">
|
||||
<% } %>
|
||||
@@ -192,6 +192,17 @@
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
|
||||
<% if (package.dist && package.dist.tarball) { %>
|
||||
<tr>
|
||||
<th>Download</th>
|
||||
<td>
|
||||
<a class="downloadlink" target="_blank" href="<%= package.dist.tarball %>">
|
||||
<%= package.dist.tarball %>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
</table>
|
||||
|
||||
<div class="details">
|
||||
|
||||
@@ -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