Compare commits

...

76 Commits
2.1.0 ... 2.3.0

Author SHA1 Message Date
fengmk2
2a3b629a9c Release 2.3.0 2015-10-15 17:55:48 +08:00
fengmk2
9a4a7c6bf0 Merge pull request #684 from ibigbug/ant-design-fix
Ant design fix
2015-10-15 16:36:47 +08:00
Yuwei Ba
550024176f Add dev dependencies. 2015-10-15 15:22:34 +08:00
Yuwei Ba
90485d33dd Package page fix. 2015-10-15 14:57:14 +08:00
fengmk2
02d9bd923e refactor: add more sync log 2015-10-15 14:48:02 +08:00
Yuwei Ba
a256e044c5 Add mock data. 2015-10-15 14:35:20 +08:00
Yiyu He
a194fc8f3c Merge pull request #681 from cnpm/greenkeeper-koa-1.1.0
Update koa to version 1.1.0 🚀
2015-10-15 14:08:28 +08:00
Yiyu He
0f61c71e2b Merge pull request #682 from cnpm/greenkeeper-should-7.1.0
Update should to version 7.1.0 🚀
2015-10-15 14:08:12 +08:00
Yiyu He
2acfc52057 Merge pull request #683 from cnpm/greenkeeper-debug-2.2.0
Update debug to version 2.2.0 🚀
2015-10-15 14:07:45 +08:00
fengmk2
e144506634 refactor: add more sync log 2015-10-15 13:27:21 +08:00
Yuwei Ba
cc9b547c86 Fix sidebar overflow. 2015-10-15 12:25:24 +08:00
fengmk2
e8bea69a4d Merge pull request #680 from ibigbug/ant-design
Try to use ant design style
2015-10-15 11:47:00 +08:00
Yuwei Ba
cd36984a56 Clean code. 2015-10-15 11:39:02 +08:00
Yuwei Ba
ec00eeee75 Indent. 2015-10-15 11:38:04 +08:00
greenkeeperio-bot
571aea5277 chore(package): update debug to version 2.2.0
http://greenkeeper.io/
2015-10-15 07:51:41 +08:00
greenkeeperio-bot
9e9fd84e12 chore(package): update should to version 7.1.0
http://greenkeeper.io/
2015-10-15 00:39:54 +08:00
greenkeeperio-bot
dbe0f96855 chore(package): update koa to version 1.1.0
http://greenkeeper.io/
2015-10-14 22:48:35 +08:00
Yuwei Ba
66f17619e6 Remove default adBanner. 2015-10-14 21:32:55 +08:00
Yuwei Ba
1d491a244b Package pages. 2015-10-14 21:22:08 +08:00
Yuwei Ba
bbb6a59441 Common styles. 2015-10-14 21:22:08 +08:00
Yuwei Ba
76e0563308 search page. 2015-10-14 21:22:08 +08:00
Yuwei Ba
5e969cc305 Sync page. 2015-10-14 21:22:08 +08:00
Yuwei Ba
9d886b0599 Profile page antd style. 2015-10-14 21:22:08 +08:00
Yuwei Ba
cd2e96f460 Unpublished pkg page style. 2015-10-14 21:22:08 +08:00
Yuwei Ba
f86734674d Add page title for unpubed pkg. 2015-10-14 21:22:08 +08:00
Yuwei Ba
e2c37d312f Index page style use antd. 2015-10-14 21:22:08 +08:00
Yiyu He
689e7e1c08 Merge pull request #677 from cnpm/greenkeeper-moment-2.10.6
Update moment to version 2.10.6 🚀
2015-10-14 18:11:54 +08:00
Yiyu He
cd6f78d91b Merge pull request #678 from cnpm/greenkeeper-cfork-1.3.1
Update cfork to version 1.3.1 🚀
2015-10-14 18:11:40 +08:00
Yiyu He
b832c91d21 Merge pull request #679 from cnpm/greenkeeper-pg-4.4.2
Update pg to version 4.4.2 🚀
2015-10-14 18:11:25 +08:00
greenkeeperio-bot
646c830a68 chore(package): update pg to version 4.4.2
http://greenkeeper.io/
2015-10-14 18:00:54 +08:00
greenkeeperio-bot
e543f8578b chore(package): update cfork to version 1.3.1
http://greenkeeper.io/
2015-10-14 17:44:48 +08:00
greenkeeperio-bot
5ff7e2867f chore(package): update moment to version 2.10.6
http://greenkeeper.io/
2015-10-14 14:28:31 +08:00
fengmk2
98e8e7d498 Merge pull request #671 from ibigbug/use-qn-dn
feat(badge): Use qiniu cdn
2015-10-08 18:20:26 +08:00
Yuwei Ba
b7eb9f8374 feat(badge): Use qiniu cdn
Issue #658
2015-10-08 17:28:36 +08:00
fengmk2
12a8eff1f6 Release 2.2.1 2015-09-30 17:30:51 +08:00
fengmk2
9156be88a9 Merge pull request #666 from cnpm/support-nfs.url-generator
fix: support nfs.url is generator
2015-09-30 17:29:31 +08:00
fengmk2
cebbf7dda2 Merge pull request #669 from cnpm/try_catch
move out try/catch block
2015-09-30 17:25:25 +08:00
fengmk2
ea226e8ad6 test: use istanbul 2015-09-30 17:22:14 +08:00
Jackson Tian
eb3b79cae2 move out try/catch block 2015-09-30 17:01:10 +08:00
fengmk2
49b70b4b4d fix: support nfs.url is generator 2015-09-29 20:13:58 +08:00
fengmk2
af724960c2 Release 2.2.0 2015-09-29 01:28:32 +08:00
fengmk2
79797376d3 Merge pull request #663 from cnpm/list-package-by-username
feat: list packages by username
2015-09-28 14:05:26 +08:00
fengmk2
7fd264440e feat: list packages by username
GET /-/users/:user/packages

return user packages list

{
  "user": {
    "name": $name
  },
  "packages": [
    {
      "name": $name,
      "description": $description,
      "version": $version
    }, ...
  ],
}

closes #661
2015-09-28 13:58:02 +08:00
fengmk2
1982b9404e Merge pull request #656 from cnpm/badge-subject
feat(badge): support custom subject
2015-09-08 11:01:29 +08:00
fengmk2
4bea6f4e5f test: use codecov 2015-09-08 10:58:53 +08:00
fengmk2
3dd04b6aac feat(badge): support custom subject
https://cnpmjs.org/badge/v/npm.svg?tag=beta&subject=偏右
=>
https://img.shields.io/badge/%E5%81%8F%E5%8F%B3-3.3.1-blue.svg?style=flat-square

closes #655
2015-09-08 10:51:34 +08:00
fengmk2
ec605efc9c Merge pull request #653 from cnpm/since-tool
feat(sync): add sync scripts
2015-09-08 10:45:52 +08:00
fengmk2
7f27447dac fix(sync): add recover logic 2015-09-08 08:03:28 +08:00
fengmk2
a2c151f246 feat(sync): add sync scripts 2015-09-06 14:50:12 +08:00
fengmk2
35efc132b3 Release 2.1.5 2015-09-05 22:00:41 +08:00
fengmk2
dd37527651 Merge pull request #652 from cnpm/filter-update-packages-by-modified
fix: only sync update packages
2015-09-05 21:48:35 +08:00
fengmk2
a82e772097 fix: only sync update packages 2015-09-05 21:41:27 +08:00
fengmk2
70fb543e7a Release 2.1.4 2015-09-05 18:00:07 +08:00
fengmk2
bbfc3e57db Merge pull request #651 from cnpm/support-old-map
fix: support new array and old map format both
2015-09-05 17:52:42 +08:00
fengmk2
9b9ec50ccf fix: support new array and old map format both 2015-09-05 17:49:31 +08:00
fengmk2
4098d966b8 Merge pull request #650 from cnpm/fetch-update-package-return-data-change
fix: /-/all/since had been redirect to /-/all/static/today.json
2015-09-05 17:38:51 +08:00
fengmk2
6e7d528c9f fix: /-/all/since had been redirect to /-/all/static/today.json
data format from map change to array

http://weibo.com/1400854834/Cz8OkaAQL
2015-09-05 17:34:39 +08:00
fengmk2
701abe831f Merge pull request #646 from cnpm/fix-etag
fix(list): let koa-etag to caculate the etag
2015-08-23 00:28:10 +08:00
dead_horse
449deab2ed fix(list): let koa-etag to caculate the etag 2015-08-23 00:20:28 +08:00
fengmk2
20f1ab3af8 Release 2.1.3 2015-08-18 10:53:44 +08:00
fengmk2
a932b6487d Merge pull request #644 from cnpm/fix-public-scope-package-store-filepath
fix: sync public scope package download url is wrong
2015-08-18 10:22:15 +08:00
fengmk2
8f05a6e8b9 fix: sync public scope package download url is wrong 2015-08-18 10:16:45 +08:00
fengmk2
4a91795288 fix: default registry change to taobao registry 2015-08-14 16:25:35 +08:00
fengmk2
de72396cd2 Release 2.1.2 2015-08-09 02:03:04 +08:00
fengmk2
7f48d45e0d Merge pull request #640 from cnpm/add-downloads-badge
feat(web): add downloads badge
2015-08-09 02:02:15 +08:00
fengmk2
553c1daf85 fix(syncer): sync worker pkg null bug 2015-08-09 01:59:23 +08:00
fengmk2
084b83e8d7 feat(web): add downloads badge
http://cnpmjs.org/badge/d/{name}.svg
2015-08-09 01:48:42 +08:00
fengmk2
05e5ac86ba Release 2.1.1 2015-07-27 12:34:47 +08:00
fengmk2
2a530a3d6e Merge pull request #634 from cnpm/fix-private-package
fix: fix private scope package detect
2015-07-27 12:32:30 +08:00
fengmk2
bde9de9f90 fix: fix private scope package detect 2015-07-27 12:28:04 +08:00
Yiyu He
d538658be9 Merge pull request #631 from cnpm/sync-public-scope-package
fix(sync): support sync public scope package
2015-07-27 10:48:59 +08:00
fengmk2
81a010bc53 fix: dont sync if upstream is npm registry 2015-07-27 01:24:33 +08:00
fengmk2
7f2c5725e5 fix(sync): support sync public scope package
e.g.: @sindresorhus/df

closes cnpm/cnpm#64
closes cnpm/cnpm#61
2015-07-27 01:20:30 +08:00
fengmk2
7f89b3be92 test: fix fails tests 2015-07-26 20:10:48 +08:00
fengmk2
1c6eb50e05 fix: ignore 503 server error 2015-07-26 20:02:48 +08:00
fengmk2
aa8d4ac850 fix: ignore sync 503 server error 2015-07-26 19:54:07 +08:00
61 changed files with 1362 additions and 545 deletions

View File

@@ -2,3 +2,6 @@ node_modules/
coverage/
.tmp/
.git/
tools/
public/
test/

View File

@@ -1,8 +1,11 @@
sudo: false
language: node_js
node_js:
- 'iojs-1.2'
- '0.12'
- '1'
- '2'
- '4'
addons:
- postgresql: '9.3'
script: 'make test-travis-all'
after_script: 'npm install coveralls@2 && cat ./coverage/lcov.info | coveralls'
after_script:
- "npm i codecov.io && cat ./coverage/coverage.json | ./node_modules/codecov.io/bin/codecov.io.js"

View File

@@ -5,3 +5,5 @@ 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)
afc163 <afc163@gmail.com> (https://github.com/afc163)
Yuwei Ba <i@xiaoba.me> (https://github.com/ibigbug)

View File

@@ -1,4 +1,83 @@
2.3.0 / 2015-10-15
==================
* Add dev dependencies.
* Package page fix.
* refactor: add more sync log
* Add mock data.
* refactor: add more sync log
* Fix sidebar overflow.
* Merge pull request #680 from ibigbug/ant-design
* Clean code.
* Indent.
* chore(package): update debug to version 2.2.0
* chore(package): update should to version 7.1.0
* chore(package): update koa to version 1.1.0
* Remove default adBanner.
* Package pages.
* Common styles.
* search page.
* Sync page.
* Profile page antd style.
* Unpublished pkg page style.
* Add page title for unpubed pkg.
* Index page style use antd.
* chore(package): update pg to version 4.4.2
* chore(package): update cfork to version 1.3.1
* chore(package): update moment to version 2.10.6
* feat(badge): Use qiniu cdn
2.2.1 / 2015-09-30
==================
* test: use istanbul
* pref: move out try/catch block
* fix: support nfs.url is generator
2.2.0 / 2015-09-29
==================
* feat: list packages by username
* test: use codecov
* feat(badge): support custom subject
* fix(sync): add recover logic
* feat(sync): add sync scripts
2.1.5 / 2015-09-05
==================
* fix: only sync update packages
2.1.4 / 2015-09-05
==================
* fix: support new array and old map format both
* fix: /-/all/since had been redirect to /-/all/static/today.json
* fix(list): let koa-etag to caculate the etag
2.1.3 / 2015-08-18
==================
* fix: sync public scope package download url is wrong
* fix: default registry change to taobao registry
2.1.2 / 2015-08-09
==================
* fix(syncer): sync worker pkg null bug
* feat(web): add downloads badge
2.1.1 / 2015-07-27
==================
* fix: fix private scope package detect
* fix: dont sync if upstream is npm registry
* fix(sync): support sync public scope package
* test: fix fails tests
* fix: ignore 503 server error
* fix: ignore sync 503 server error
2.1.0 / 2015-07-08
==================

View File

@@ -3,12 +3,12 @@ REPORTER = spec
TIMEOUT = 30000
MOCHA_OPTS =
DB = sqlite
DISTURL = http://npm.taobao.org/mirrors/iojs
DISTURL = https://npm.taobao.org/mirrors/iojs
BIN = iojs
ifeq ($(findstring io.js, $(shell which node)),)
BIN = node
DISTURL = http://npm.taobao.org/mirrors/node
DISTURL = https://npm.taobao.org/mirrors/node
endif
install:
@@ -56,7 +56,7 @@ test-pg: init-pg
test-all: test-sqlite test-mysql
test-cov cov: install init-database
@NODE_ENV=test DB=${DB} $(BIN) --harmony \
@NODE_ENV=test DB=${DB} $(BIN) \
node_modules/.bin/istanbul cover --preserve-comments \
node_modules/.bin/_mocha \
-- -u exports \
@@ -77,10 +77,9 @@ test-cov-mysql: init-mysql
test-travis: install init-database
@NODE_ENV=test DB=${DB} CNPM_SOURCE_NPM=http://registry.npmjs.com CNPM_SOURCE_NPM_ISCNPM=false \
$(BIN) --harmony \
$(BIN) \
node_modules/.bin/istanbul cover --preserve-comments \
node_modules/.bin/_mocha \
--report lcovonly \
-- -u exports \
--reporter dot \
--timeout $(TIMEOUT) \

View File

@@ -3,29 +3,20 @@ cnpmjs.org
[![NPM version][npm-image]][npm-url]
[![build status][travis-image]][travis-url]
[![Test coverage][coveralls-image]][coveralls-url]
[![Gittip][gittip-image]][gittip-url]
[![Test coverage][cov-image]][cov-url]
[![David deps][david-image]][david-url]
[![node version][node-image]][node-url]
[![npm download][download-image]][download-url]
[![gitter][gitter-image]][gitter-url]
[npm-image]: http://cnpmjs.org/badge/v/cnpmjs.org.svg?style=flat-square
[npm-url]: http://cnpmjs.org/package/cnpmjs.org
[travis-image]: https://img.shields.io/travis/cnpm/cnpmjs.org.svg?style=flat-square
[travis-url]: https://travis-ci.org/cnpm/cnpmjs.org
[coveralls-image]: https://img.shields.io/coveralls/cnpm/cnpmjs.org.svg?style=flat-square
[coveralls-url]: https://coveralls.io/r/cnpm/cnpmjs.org?branch=master
[gittip-image]: https://img.shields.io/gittip/fengmk2.svg?style=flat-square
[gittip-url]: https://www.gittip.com/fengmk2/
[cov-image]: http://codecov.io/github/cnpm/cnpmjs.org/coverage.svg?branch=master
[cov-url]: http://codecov.io/github/cnpm/cnpmjs.org?branch=master
[david-image]: https://img.shields.io/david/cnpm/cnpmjs.org.svg?style=flat-square
[david-url]: https://david-dm.org/cnpm/cnpmjs.org
[node-image]: https://img.shields.io/badge/node.js-%3E=_0.11-red.svg?style=flat-square
[node-url]: http://nodejs.org/download/
[download-image]: https://img.shields.io/npm/dm/cnpmjs.org.svg?style=flat-square
[download-url]: https://npmjs.org/package/cnpmjs.org
[gitter-image]: https://badges.gitter.im/Join%20Chat.svg
[gitter-url]: https://gitter.im/cnpm/cnpmjs.org?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
![logo](https://raw.github.com/cnpm/cnpmjs.org/master/logo.png)
@@ -120,8 +111,8 @@ Tips: make sure your code is following the [node-style-guide](https://github.com
## Sponsors
- [![UCloud云计算](https://www.ucloud.cn/www/img/www/logo.png)](http://www.ucloud.cn/sdk?sem=sdk-CNPMJS)
- [![UCloud云计算](https://www.ucloud.cn/static/style/images/about/logo.png)](http://www.ucloud.cn?sem=sdk-CNPMJS)
## License
MIT
[MIT](LICENSE.txt)

View File

@@ -1,11 +1,9 @@
/*!
* cnpmjs.org - common/nfs.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/
'use strict';

View File

@@ -1,6 +1,4 @@
/**!
* cnpmjs.org - config/index.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
@@ -161,7 +159,7 @@ var config = {
enablePrivate: false,
// registry scopes, if don't set, means do not support scopes
scopes: [ '@cnpm', '@cnpmtest' ],
scopes: [ '@cnpm', '@cnpmtest', '@cnpm-test' ],
// some registry already have some private packages in global scope
// but we want to treat them as scoped private packages,

View File

@@ -1,11 +1,9 @@
/**!
* cnpmjs.org - controllers/registry/package/download.js
*
* Copyright(c) fengmk2 and other contributors.
* Copyright(c) cnpm and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/
'use strict';
@@ -18,6 +16,7 @@ var debug = require('debug')('cnpmjs.org:controllers:registry:download');
var mime = require('mime');
var utility = require('utility');
var defer = require('co-defer');
var is = require('is-type-of');
var nfs = require('../../../common/nfs');
var logger = require('../../../common/logger');
var common = require('../../../lib/common');
@@ -32,12 +31,16 @@ module.exports = function* download(next) {
var name = this.params.name || this.params[0];
var filename = this.params.filename || this.params[1];
var version = filename.slice(name.length + 1, -4);
var row = yield* packageService.getModule(name, version);
var row = yield packageService.getModule(name, version);
// can not get dist
var url = null;
if (typeof nfs.url === 'function') {
url = nfs.url(common.getCDNKey(name, filename));
if (is.generatorFunction(nfs.url)) {
url = yield nfs.url(common.getCDNKey(name, filename));
} else {
url = nfs.url(common.getCDNKey(name, filename));
}
}
debug('download %s %s %s %s', name, filename, version, url);
@@ -78,7 +81,7 @@ module.exports = function* download(next) {
this.attachment(filename);
this.etag = dist.shasum;
this.body = yield* downloadAsReadStream(dist.key);
this.body = yield downloadAsReadStream(dist.key);
};
defer.setInterval(function* () {

View File

@@ -45,9 +45,6 @@ module.exports = function* list() {
}
}
// use modifiedTime as etag
this.set('ETag', '"' + modifiedTime.getTime() + '"');
// must set status first
this.status = 200;
if (this.fresh) {

View File

@@ -0,0 +1,29 @@
/**!
* list packages by username
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <m@fengmk2.com> (http://fengmk2.com)
*/
'use strict';
/**
* Module dependencies.
*/
const packageService = require('../../../services/package');
module.exports = function*() {
const username = this.params.user;
const packages = yield packageService.listModulesByUser(username);
this.body = {
user: {
name: username,
},
packages: packages,
};
};

View File

@@ -21,7 +21,7 @@ var config = require('../config');
exports.sync = function* () {
var username = this.user.name || 'anonymous';
var name = this.params.name;
var name = this.params.name || this.params[0];
var type = 'package';
if (name.indexOf(':') > 0) {
// user:fengmk2
@@ -60,7 +60,7 @@ exports.sync = function* () {
};
exports.getSyncLog = function* (next) {
var logId = this.params.id;
var logId = this.params.id || this.params[1];
var offset = Number(this.query.offset) || 0;
var row = yield* Log.get(logId);
if (!row) {

View File

@@ -1,11 +1,9 @@
/**!
* cnpmjs.org - controllers/sync_module_worker.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
@@ -196,6 +194,11 @@ SyncModuleWorker.prototype._doneOne = function* (concurrencyId, name, success) {
};
SyncModuleWorker.prototype.syncUpstream = function* (name) {
if (config.sourceNpmRegistry.indexOf('registry.npmjs.org') >= 0 ||
config.sourceNpmRegistry.indexOf('registry.npmjs.com') >= 0) {
this.log('----------------- upstream is npm registry: %s, ignore it -------------------', config.sourceNpmRegistry);
return;
}
var syncname = name;
if (this.type === 'user') {
syncname = this.type + ':' + syncname;
@@ -302,17 +305,17 @@ SyncModuleWorker.prototype.next = function* (concurrencyId) {
this.log('----------------- Syncing %s -------------------', name);
// ignore scoped package
if (name[0] === '@') {
this.log('[c#%d] [%s] ignore sync scoped package',
concurrencyId, name);
// ignore private scoped package
if (common.isPrivateScopedPackage(name)) {
this.log('[c#%d] [%s] ignore sync private scoped %j package',
concurrencyId, name, config.scopes);
yield* this._doneOne(concurrencyId, name, true);
return;
}
// get from npm
try {
var result = yield* npmSerivce.request('/' + name);
var result = yield* npmSerivce.request('/' + name.replace('/', '%2f'));
pkg = result.data;
status = result.status;
} catch (err) {
@@ -329,7 +332,7 @@ SyncModuleWorker.prototype.next = function* (concurrencyId) {
var unpublishedInfo = null;
if (status === 404) {
// check if it's unpublished
if (pkg.time && pkg.time.unpublished && pkg.time.unpublished.time) {
if (pkg && pkg.time && pkg.time.unpublished && pkg.time.unpublished.time) {
unpublishedInfo = pkg.time.unpublished;
} else {
pkg = null;
@@ -869,6 +872,7 @@ SyncModuleWorker.prototype._sync = function* (name, pkg) {
};
SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePackage) {
logger.syncInfo('[sync_module_worker] start sync %s@%s', sourcePackage.name, sourcePackage.version);
var that = this;
var username = this.username;
var downurl = sourcePackage.dist.tarball;
@@ -925,6 +929,7 @@ SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePack
try {
// get tarball
logger.syncInfo('[sync_module_worker] downloading %j to %j', downurl, filepath);
var r = yield urllib.request(downurl, options);
var statusCode = r.status || -1;
// https://github.com/cnpm/cnpmjs.org/issues/325
@@ -975,8 +980,17 @@ SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePack
};
// upload to NFS
logger.syncInfo('[sync_module_worker] uploading %j to nfs', options);
var result = yield nfs.upload(filepath, options);
return yield *afterUpload(result);
var result;
try {
result = yield nfs.upload(filepath, options);
} catch (err) {
logger.syncInfo('[sync_module_worker] upload %j to nfs error: %s', err);
throw err;
}
logger.syncInfo('[sync_module_worker] uploaded, saving %j to database', result);
var r = yield *afterUpload(result);
logger.syncInfo('[sync_module_worker] sync %s@%s done!', sourcePackage.name, sourcePackage.version);
return r;
} finally {
// remove tmp file whatever
fs.unlink(filepath, utility.noop);

View File

@@ -5,7 +5,7 @@
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/
'use strict';
@@ -18,6 +18,7 @@ var utility = require('utility');
var util = require('util');
var config = require('../../config');
var packageService = require('../../services/package');
var DownloadTotal = require('../../services/download_total');
exports.version = function* () {
var color = 'lightgrey';
@@ -40,9 +41,22 @@ exports.version = function* () {
}
var subject = config.badgeSubject.replace(/\-/g, '--');
if (this.query.subject) {
subject = this.query.subject.replace(/\-/g, '--');
}
version = version.replace(/\-/g, '--');
var style = this.query.style || 'flat-square';
var url = util.format('https://img.shields.io/badge/%s-%s-%s.svg?style=%s',
subject, version, color, utility.encodeURIComponent(style));
var url = util.format('https://dn-img-shields-io.qbox.me/badge/%s-%s-%s.svg?style=%s',
utility.encodeURIComponent(subject), version, color, utility.encodeURIComponent(style));
this.redirect(url);
};
exports.downloads = function* () {
// https://dn-img-shields-io.qbox.me/badge/downloads-100k/month-brightgreen.svg?style=flat-square
var name = this.params[0];
var count = yield DownloadTotal.getTotalByName(name);
var style = this.query.style || 'flat-square';
var url = util.format('https://dn-img-shields-io.qbox.me/badge/downloads-%s-brightgreen.svg?style=%s',
count, utility.encodeURIComponent(style));
this.redirect(url);
};

View File

@@ -68,7 +68,8 @@ module.exports = function* show(next) {
}
}
yield this.render('package_unpublished', {
package: data
package: data,
title: 'Package - ' + name
});
return;
}
@@ -178,7 +179,7 @@ module.exports = function* show(next) {
pkg.engines[k] = {
version: engine,
title: k + ': ' + engine,
badgeURL: 'https://img.shields.io/badge/' + encodeURIComponent(k) +
badgeURL: 'https://dn-img-shields-io.qbox.me/badge/' + encodeURIComponent(k) +
'-' + encodeURIComponent(engine) + '-' + color + '.svg?style=flat-square',
};
}

View File

@@ -1,12 +1,10 @@
/**!
* cnpmjs.org - dispatch.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com>
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/
'use strict';

View File

@@ -9,6 +9,7 @@ So `cnpm` is meaning: **Company npm**.
- [Node.js](https://nodejs.org) version: <span id="node-version"></span>
- For developers behind the GFW, please visit [the Chinese mirror](https://npm.taobao.org). 中国用户请访问[国内镜像站点](https://npm.taobao.org/)。
<div class="ant-table">
<table class="downloads">
<tbody>
<tr>
@@ -28,12 +29,17 @@ So `cnpm` is meaning: **Company npm**.
</tr>
</tbody>
</table>
</div>
<div class="sync" style="display:none;">
<h3>Sync Status</h3>
<p id="sync-model"></p>
<p>Last sync time is <span id="last-sync-time"></span>. </p>
<p class="syncing alert alert-info">The sync worker is working in the backend now. </p>
<div class="ant-alert ant-alert-info">
<span class="anticon ant-alert-icon anticon-info-circle"></span>
<span class="syncing ant-alert-description">The sync worker is working in the backend now. </span>
</div>
<div class="ant-table">
<table class="sync-status">
<tbody>
<tr>
@@ -48,40 +54,47 @@ So `cnpm` is meaning: **Company npm**.
</tr>
</tbody>
</table>
</div>
</div>
<script src="/js/readme.js"></script>
## Version Badge
## Badges
Default style is `flat-square`.
Badge URL: `https://cnpmjs.org/badge/v/cnpmjs.org.svg` ![cnpmjs.org-badge](//cnpmjs.org/badge/v/cnpmjs.org.svg)
### Version
* `<0.1.0 & >=0.0.0`: ![red-badge](https://img.shields.io/badge/cnpm-0.0.1-red.svg?style=flat-square)
* `<1.0.0 & >=0.1.0`: ![red-badge](https://img.shields.io/badge/cnpm-0.1.0-green.svg?style=flat-square)
* `>=1.0.0`: ![red-badge](https://img.shields.io/badge/cnpm-1.0.0-blue.svg?style=flat-square)
Badge URL: `http://cnpmjs.org/badge/v/cnpmjs.org.svg` ![cnpmjs.org-version-badge](//cnpmjs.org/badge/v/cnpmjs.org.svg)
* `<0.1.0 & >=0.0.0`: ![red-badge](https://dn-img-shields-io.qbox.me/badge/cnpm-0.0.1-red.svg?style=flat-square)
* `<1.0.0 & >=0.1.0`: ![red-badge](https://dn-img-shields-io.qbox.me/badge/cnpm-0.1.0-green.svg?style=flat-square)
* `>=1.0.0`: ![red-badge](https://dn-img-shields-io.qbox.me/badge/cnpm-1.0.0-blue.svg?style=flat-square)
### Downloads
Badge URL: `http://cnpmjs.org/badge/d/cnpmjs.org.svg` ![cnpmjs.org-download-badge](//cnpmjs.org/badge/d/cnpmjs.org.svg)
## Usage
use our npm client [cnpm](https://github.com/cnpm/cnpm)(More suitable with cnpmjs.org and gzip support), you can get our client through npm:
```bash
$ npm install -g cnpm --registry=https://r.cnpmjs.org
$ npm install -g cnpm --registry=http://registry.npm.taobao.org
```
Or you can alias NPM to use it:
```bash
alias cnpm="npm --registry=https://r.cnpmjs.org \
alias cnpm="npm --registry=http://registry.npm.taobao.org \
--cache=$HOME/.npm/.cache/cnpm \
--disturl=https://cnpmjs.org/mirrors/node \
--disturl=http://registry.npm.taobao.org/mirrors/node \
--userconfig=$HOME/.cnpmrc"
#Or alias it in .bashrc or .zshrc
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=https://r.cnpmjs.org \
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=http://registry.npm.taobao.org \
--cache=$HOME/.npm/.cache/cnpm \
--disturl=https://cnpmjs.org/mirrors/node \
--disturl=http://registry.npm.taobao.org/mirrors/node \
--userconfig=$HOME/.cnpmrc"' >> ~/.zshrc && source ~/.zshrc
```
@@ -104,7 +117,7 @@ $ cnpm sync connect
sync package on web: [sync/connect](/sync/connect)
```bash
$ open https://cnpmjs.org/sync/connect
$ open http://registry.npm.taobao.org/sync/connect
```
### publish / unpublish
@@ -138,4 +151,4 @@ Release [History](/history).
## Sponsors
- [![UCloud云计算](https://www.ucloud.cn/www/img/www/logo.png)](http://www.ucloud.cn/sdk?sem=sdk-CNPMJS)
- [![UCloud云计算](https://www.ucloud.cn/static/style/images/about/logo.png)](http://www.ucloud.cn?sem=sdk-CNPMJS)

View File

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

View File

@@ -27,6 +27,11 @@ exports.getTarballFilepath = function (filename) {
};
exports.getCDNKey = function (name, filename) {
// if name is scope package name, need to auto fix filename as a scope package file name
// e.g.: @scope/foo, filename: foo-1.0.0.tgz => filename: @scope/foo-1.0.0.tgz
if (name[0] === '@' && filename[0] !== '@') {
filename = name.split('/')[0] + '/' + filename;
}
return '/' + name + '/-/' + filename;
};
@@ -66,3 +71,10 @@ exports.isLocalModule = function (mods) {
}
return false;
};
exports.isPrivateScopedPackage = function (name) {
if (name[0] !== '@') {
return false;
}
return config.scopes.indexOf(name.split('/')[0]) >= 0;
};

View File

@@ -1,11 +1,9 @@
/**!
* cnpmjs.org - models/index.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/
'use strict';
@@ -15,7 +13,6 @@
*/
var path = require('path');
var Sequelize = require('sequelize');
var sequelize = require('../common/sequelize');
function load(name) {

View File

@@ -1,6 +1,6 @@
{
"name": "cnpmjs.org",
"version": "2.1.0",
"version": "2.3.0",
"description": "Private npm registry and web for Enterprise, base on MySQL and Simple Store Service",
"main": "index.js",
"scripts": {
@@ -16,14 +16,14 @@
"agentkeepalive": "~1.2.0",
"bluebird": "~2.9.7",
"bytes": "~1.0.0",
"cfork": "~1.2.2",
"cfork": "~1.3.1",
"co": "~4.3.1",
"co-defer": "~1.0.0",
"co-gather": "~0.0.1",
"co-sleep": "~0.0.1",
"commander": "~2.6.0",
"copy-to": "~2.0.1",
"debug": "~2.1.1",
"debug": "~2.2.0",
"error-formater": "~1.0.3",
"fs-cnpm": "~1.2.0",
"giturl": "~0.0.3",
@@ -32,8 +32,9 @@
"gravatar": "~1.1.0",
"humanize-ms": "~1.0.1",
"humanize-number": "~0.0.2",
"is-type-of": "~0.3.1",
"kcors": "~1.0.1",
"koa": "~0.17.0",
"koa": "~1.1.0",
"koa-limit": "~1.0.2",
"koa-markdown": "~2.0.1",
"koa-middlewares": "~2.1.0",
@@ -43,7 +44,7 @@
"mime": "~1.3.4",
"mini-logger": "~1.0.0",
"mkdirp": "~0.5.0",
"moment": "~2.9.0",
"moment": "~2.10.6",
"mysql": "~2.5.4",
"nodemailer": "~1.3.0",
"semver": "~4.2.0",
@@ -60,15 +61,15 @@
"chunkstream": "*",
"co-mocha": "*",
"contributors": "*",
"istanbul-harmony": "*",
"istanbul": "*",
"jshint": "*",
"mm": "*",
"mocha": "*",
"node-dev": "*",
"pedding": "*",
"pg": "~4.2.0",
"pg": "~4.4.2",
"pg-hstore": "~2.3.1",
"should": "~4.0.4",
"should": "~7.1.0",
"should-http": "*",
"sqlite3": "*",
"supertest": "*"
@@ -91,7 +92,7 @@
"registry"
],
"engines": {
"node": ">= 0.11.14"
"node": ">= 1.0.0"
},
"author": [
"fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)",

1
public/css/antd-0.9.1.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,28 +1,127 @@
body {
font-size: 16px;
color: #333333;
}
header {
max-width: 1140px;
min-width: 960px;
margin: 30px auto 30px auto;
.mb15 {
margin-bottom: 15px !important;
}
header .logo {
.container {
min-width: 960px;;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto
}
@media (min-width: 768px) {
.container {
width:750px
}
}
@media (min-width: 992px) {
.container {
width:970px
}
}
@media (min-width: 1200px) {
.container {
width:1170px
}
}
hr {
margin-top: 20px;
margin-bottom: 20px;
border: 0;
border-top: 1px solid #eee;
height: 0;
}
#header {
padding: 30px 0;
height: 156px;
}
#header .logo {
display: inline-block;
}
header #search-input {
#header .logo a {
display: inline-block;
vertical-align: top;
}
#search-input {
display: inline-block;
width: 400px;
height: 50px;
font-size: 30px;
font-size: 20px;
float: right;
margin-top: 22px;
border-radius: 8px;
padding: 10px 20px;
}
#search .package.match {
font-weight: bold;
}
.bottom {
text-align: center;
font-size: 14px;
padding-bottom: 15px;
}
code {
padding: 2px 4px;
font-size: 95%;
color: #c7254e;
background-color: #f9f2f4;
border-radius: 4px;
}
img {
vertical-align: middle;
}
pre {
margin-bottom: 20px;
background: #333;
color: #fff;
border-radius: 6px;
margin-top: 30px;
padding: 10px 20px;
word-wrap: break-word;
}
pre code {
padding: 0;
font-size: inherit;
color: inherit;
white-space: pre-wrap;
background-color: transparent;
border-radius: 0;
}
.ant-table {
font-size: 14px;
}
.ant-alert {
font-size: 14px !important;
padding: 15px;
}
.pack-overview {
min-width: 960px;
color: #f1f1f1;
height: 300px;
width: 100%;
margin-bottom: 50px;
position: relative;
}
.pack-overview-background {
height: 300px;
@@ -34,13 +133,18 @@ header #search-input {
}
.pack-overview a {
color: #f1f1f1;
text-decoration: underline;
text-decoration: none;
font-size: 16px;
transition: all .2s ease-in-out;
padding-bottom: 2px;
border-bottom: 1px solid;
}
.pack-overview a:hover {
color: #ffffff;
opacity: .7;
}
.pack-info {
position: relative;
max-width: 1140px;
margin: 0 auto;
padding: 30px 0;
@@ -53,133 +157,125 @@ header #search-input {
line-height: 48px;
color: #fff;
}
.pack-description {
line-height: 28px;
text-overflow: ellipsis;
color: #fff;
font-family: "-apple-system", sans-serif;
margin-top: 20px;
display: block; /* Fallback for non-webkit */
display: -webkit-box;
width: 600px;
white-space:nowrap;
font-size: 20px;
line-height: 26px;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
overflow:hidden;
max-height: 100px;
}
.deprecated {
text-decoration: line-through;
color: #BDBDBD;
}
.deprecated-content {
color: #FF8A65;
color: #555;
width: 600px;
white-space:nowrap;
text-overflow: ellipsis;
overflow:hidden;
font-style: italic;
}
.pack-ver {
font-size: 28px;
line-height: 48px;
}
.pack-repo {
margin-top: 50px;
.pack-lastup {
font-size: 14px;
display: inline-block;
}
.pack-repo {
position: absolute;
bottom: 10px;
text-align: right;
width: 100%;
}
.pack-repo a {
border: none;
}
.pack-repo a:hover {
border-bottom: 1px solid;
}
.pack-install {
position: absolute;
top: 85px;
top: 75px;
right: 0px;
max-width: 450px;
}
.pack-install pre {
border: none;
background-color: #666666;
color: #f1f1f1;
font-size: 16px;
width: 480px;
}
.pack-details {
max-width: 1140px;
min-width: 960px;
margin: 40px auto;
}
.pack-details a {
color: #333333;
}
.cols-box {
display: flex;
-webkit-box-pack: center;
.pack-details a:hover {
border-bottom: 1px solid;
opacity: .8;
}
.cols-box ul {
list-style: none;
margin: 0;
padding: 0;
}
.cols-box-item {
flex: 1;
height: 250px;
margin-right: 30px;
margin-bottom: 15px;
}
.cols-box-item:last-child {
margin-right: 0;
}
.cols-box-title {
font-size: 20px;
color: #428bca;
margin-bottom: 10px;
font-weight: bold;
overflow: hidden;
border-bottom: 1px solid #ccc;
padding-bottom: 3px;
}
.cols-box-item .more-item {
font-size: 16px;
line-height: 28px;
color: #428bca;
text-decoration: underline;
float: right;
margin-top: 15px;
}
.pack-dep-ver, .pack-download-num {
float: right;
}
.pack-lastup, .pack-sync {
margin-top: 10px;
}
.pack-sync a {
font-size: 23px;
}
.maintainers .row {
display: -moz-box;
display: -webkit-box;
display: box;
-webkit-box-pack: center;
margin-right: 0;
margin-left: 0;
float: left;
}
.maintainer {
-moz-box-flex: 1;
-webkit-box-flex: 1;
box-flex: 1;
margin-bottom: 10px;
width: 50%;
-webkit-box-orient: vertical;
-webkit-box-pack: center;
line-height: 50px;
line-height: 30px;
overflow: hidden;
}
.maintainer a:hover {
font-style: italic;
border: none;
}
.maintainer .avatar {
-webkit-border-radius: 50px;
-moz-border-radius: 50px;
border-radius: 50px;
border-radius: 30px;
width: 30px;
height: 30px;
margin-right: 10px;
}
.downloads .row {
margin-left: 0;
margin-right: 50px;
line-height: 28px;
border-bottom: 1px solid #f2f2f2;
}
.pack-readme {
max-width: 1140px;
min-width: 960px;
margin: 10px auto;
border-top: 1px solid #dddddd;
}
.pack-readme .title {
color: #dddddd;
font-weight: bolder;
margin-bottom: 20px;
text-align: right;
}

147
public/css/yue.css Normal file
View File

@@ -0,0 +1,147 @@
/**
* yue.css
*
* yue.css is designed for readable content.
*
* Copyright (c) 2013 - 2014 by Hsiaoming Yang.
*/
.yue h1,
.yue h2,
.yue h3,
.yue h4,
.yue h5,
.yue h6 {
font-family: "-apple-system", "Georgia", "Xin Gothic", "Hiragino Sans GB", "Droid Sans Fallback", "Microsoft YaHei", "SimSun", sans-serif;
color: #222223;
}
.yue h1 {
font-size: 1.8em;
margin: 0.67em 0;
}
.yue > h1 {
margin-top: 0;
font-size: 2em;
}
.yue h2 {
font-size: 1.5em;
margin: 0.83em 0;
}
.yue h3 {
font-size: 1.17em;
margin: 1em 0;
}
.yue h4,
.yue h5,
.yue h6 {
font-size: 1em;
margin: 1.6em 0 1em 0;
}
.yue h6 {
font-weight: 500;
}
.yue p {
margin-top: 0;
margin-bottom: 1.46em;
}
.yue strong,
.yue b {
font-weight: 700;
color: #222223;
}
.yue em,
.yue i {
font-style: italic;
color: #222223;
}
.yue hr {
display: block;
width: 14%;
margin: 40px auto 34px;
border: 0 none;
border-top: 3px solid #dededc;
}
.yue blockquote {
margin: 0 0 1.64em 0;
border-left: 3px solid #dadada;
padding-left: 12px;
color: #666664;
}
.yue blockquote a {
color: #666664;
}
.yue ul,
.yue ol {
margin: 0 0 24px 6px;
padding-left: 16px;
}
.yue ul {
list-style-type: square;
}
.yue ol {
list-style-type: decimal;
}
.yue li {
margin-bottom: 0.2em;
}
.yue li ul,
.yue li ol {
margin-top: 0;
margin-bottom: 0;
margin-left: 14px;
}
.yue li ul {
list-style-type: disc;
}
.yue li ul ul {
list-style-type: circle;
}
.yue li p {
margin: 0.4em 0 0.6em;
}
.yue .unstyled {
list-style-type: none;
margin: 0;
padding: 0;
}
@media (min-width: 1100px) {
.yue blockquote {
margin-left: -24px;
padding-left: 20px;
border-width: 4px;
}
.yue blockquote blockquote {
margin-left: 0;
}
}

View File

@@ -1,12 +1,10 @@
/**!
* cnpmjs.org - routes/registry.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com>
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/
"use strict";
@@ -36,6 +34,7 @@ var removeOneVersion = require('../controllers/registry/package/remove_version')
var updatePackage = require('../controllers/registry/package/update');
var downloadPackage = require('../controllers/registry/package/download');
var downloadTotal = require('../controllers/registry/package/download_total');
var listPackagesByUser = require('../controllers/registry/package/list_by_user');
var addUser = require('../controllers/registry/user/add');
var showUser = require('../controllers/registry/user/show');
@@ -77,7 +76,9 @@ function routes(app) {
app.put('/:name', login, publishable, savePackage);
// sync from source npm
app.put(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/sync$/, sync.sync);
app.put('/:name/sync', sync.sync);
app.get(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/sync\/log\/(\d+)$/, sync.getSyncLog);
app.get('/:name/sync/log/:id', sync.getSyncLog);
// add tag
@@ -111,6 +112,7 @@ function routes(app) {
// list all packages of user
app.get('/-/by-user/:user', userPackage.list);
app.get('/-/users/:user/packages', listPackagesByUser);
// download times
app.get('/downloads/range/:range/:name', downloadTotal);

View File

@@ -42,15 +42,19 @@ function routes(app) {
app.get('/~:name', showUser);
app.get(/\/sync\/(@[\w\-\.]+\/[\w\-\.]+)$/, showSync);
app.get('/sync/:name', showSync);
app.get('/sync', showSync);
app.put(/\/sync\/(@[\w\-\.]+\/[\w\-\.]+)$/, sync.sync);
app.put('/sync/:name', sync.sync);
app.get(/\/sync\/(@[\w\-\.]+\/[\w\-\.]+)\/log\/(\d+)$/, sync.getSyncLog);
app.get('/sync/:name/log/:id', sync.getSyncLog);
app.get('/_list/search/search', searchRange);
app.get(/^\/badge\/v\/([@\w\-\.\/]+)\.svg$/, badge.version);
app.get(/^\/badge\/d\/([@\w\-\.\/]+)\.svg$/, badge.downloads);
}
module.exports = routes;

View File

@@ -15,11 +15,12 @@
*/
var config = require('../config');
var isPrivateScopedPackage = require('../lib/common').isPrivateScopedPackage;
config.privatePackages = config.privatePackages || [];
exports.isPrivatePackage = function* (name) {
if (name[0] === '@') {
exports.isPrivatePackage = function (name) {
if (isPrivateScopedPackage(name)) {
return true;
}
if (config.privatePackages.indexOf(name) >= 0) {

View File

@@ -32,6 +32,27 @@ exports.getModuleTotal = function* (name, start, end) {
return formatRows(rows, start, end);
};
exports.getTotalByName = function* (name) {
var rows = yield DownloadTotal.findAll({
where: {
name: name
}
});
var count = 0;
rows.forEach(function (row) {
for (var i = 1; i <= 31; i++) {
var day = i < 10 ? '0' + i : String(i);
var field = 'd' + day;
var val = row[field];
if (typeof val === 'string') {
val = utility.toSafeNumber(val);
}
count += val;
}
});
return count;
};
exports.plusModuleTotal = function* (data) {
var yearMonth = parseYearMonth(data.date);
// all module download total

View File

@@ -15,6 +15,7 @@
* Module dependencies.
*/
var ms = require('humanize-ms');
var urllib = require('../common/urllib');
var config = require('../config');
@@ -39,7 +40,8 @@ function* request(url, options) {
var data = err.data || '[empty]';
if (err.name === 'JSONResponseFormatError' && statusCode >= 500) {
err.name = 'NPMServerError';
err.message = 'Status ' + statusCode + ', ' + data.toString();
err.status = statusCode;
err.message = 'Url: ' + url + ', Status ' + statusCode + ', ' + data.toString();
}
throw err;
}
@@ -68,6 +70,83 @@ exports.get = function* (name) {
return data;
};
exports.fetchUpdatesSince = function* (lastSyncTime, timeout) {
var lastModified = lastSyncTime - ms('10m');
var data = yield exports.getAllSince(lastModified, timeout);
var result = {
lastModified: lastSyncTime,
names: [],
};
if (!data) {
return result;
}
if (Array.isArray(data)) {
// support https://registry.npmjs.org/-/all/static/today.json
var maxModified;
data.forEach(function (pkg) {
if (pkg.time && pkg.time.modified) {
var modified = Date.parse(pkg.time.modified);
if (modified >= lastModified) {
result.names.push(pkg.name);
}
if (!maxModified || modified > maxModified) {
maxModified = modified;
}
} else {
result.names.push(pkg.name);
}
});
if (maxModified) {
result.lastModified = maxModified;
}
} else {
// /-/all/since
if (data._updated) {
result.lastModified = data._updated;
delete data._updated;
}
result.names = Object.keys(data);
}
return result;
};
exports.fetchAllPackagesSince = function* (timestamp) {
var r = yield request('/-/all/static/all.json', {
registry: 'http://registry.npmjs.org',
timeout: 600000
});
// {"_updated":1441520402174,"0":{"name":"0","dist-tags
// "time":{"modified":"2014-06-17T06:38:43.495Z"}
var data = r.data;
var result = {
lastModified: timestamp,
lastModifiedName: null,
names: [],
};
var maxModified;
for (var key in data) {
if (key === '_updated') {
continue;
}
var pkg = data[key];
if (!pkg.time || !pkg.time.modified) {
continue;
}
var modified = Date.parse(pkg.time.modified);
if (modified >= timestamp) {
result.names.push(pkg.name);
}
if (!maxModified || modified > maxModified) {
maxModified = modified;
result.lastModifiedName = pkg.name;
}
}
if (maxModified) {
result.lastModified = maxModified;
}
return result;
};
exports.getAllSince = function* (startkey, timeout) {
var r = yield* request('/-/all/since?stale=update_after&startkey=' + startkey, {
timeout: timeout || 300000
@@ -75,6 +154,14 @@ exports.getAllSince = function* (startkey, timeout) {
return r.data;
};
exports.getAllToday = function* (timeout) {
var r = yield* request('/-/all/static/today.json', {
timeout: timeout || 300000
});
// data is array: see https://registry.npmjs.org/-/all/static/today.json
return r.data;
};
exports.getShort = function* (timeout) {
var r = yield* request('/-/short', {
timeout: timeout || 300000,

View File

@@ -28,19 +28,23 @@ var NpmModuleMaintainer = models.NpmModuleMaintainer;
// module
var _parseRow = function (row) {
if (row.package.indexOf('%7B%22') === 0) {
// now store package will encodeURIComponent() after JSON.stringify
row.package = decodeURIComponent(row.package);
}
row.package = JSON.parse(row.package);
if (typeof row.publish_time === 'string') {
// pg bigint is string
row.publish_time = Number(row.publish_time);
}
};
// module:read
function parseRow(row) {
if (row && row.package) {
try {
if (row.package.indexOf('%7B%22') === 0) {
// now store package will encodeURIComponent() after JSON.stringify
row.package = decodeURIComponent(row.package);
}
row.package = JSON.parse(row.package);
if (typeof row.publish_time === 'string') {
// pg bigint is string
row.publish_time = Number(row.publish_time);
}
_parseRow(row);
} catch (e) {
console.warn('parse package error: %s, id: %s version: %s, error: %s', row.name, row.id, row.version, e);
}
@@ -128,7 +132,7 @@ exports.listModules = function* (names) {
id: ids
},
attributes: [
'name', 'description'
'name', 'description', 'version',
]
});
return rows;
@@ -219,7 +223,7 @@ exports.listAllPublicModuleNames = function* () {
var sql = 'SELECT DISTINCT(name) AS name FROM tag ORDER BY name';
var rows = yield models.query(sql);
return rows.filter(function (row) {
return row.name[0] !== '@';
return !common.isPrivatePackage(row.name);
}).map(function (row) {
return row.name;
});
@@ -500,8 +504,7 @@ exports.updatePrivateModuleMaintainers = function* (name, usernames) {
};
function* getMaintainerModel(name) {
var isPrivatePackage = yield* common.isPrivatePackage(name);
return isPrivatePackage ? PrivateModuleMaintainer : NpmModuleMaintainer;
return common.isPrivatePackage(name) ? PrivateModuleMaintainer : NpmModuleMaintainer;
}
exports.listMaintainers = function* (name) {

View File

@@ -142,6 +142,10 @@ function sendMailToAdmin(err, result, syncTime) {
var type;
var html;
if (err) {
// ignore 503 error
if (err.status === 503) {
return;
}
subject = 'Sync Error';
type = 'error';
html = util.format('Sync packages from official registry failed.\n' +

View File

@@ -1,4 +1,4 @@
/*!
/**!
* cnpmjs.org - sync/sync_all.js
*
* Copyright(c) cnpmjs.org and other contributors.
@@ -6,6 +6,7 @@
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <m@fengmk2.com> (http://fengmk2.com)
*/
'use strict';
@@ -14,7 +15,6 @@
* Module dependencies.
*/
var ms = require('humanize-ms');
var thunkify = require('thunkify-wrap');
var config = require('../config');
var Status = require('./status');
@@ -40,19 +40,6 @@ function* getFirstSyncPackages(lastSyncModule) {
}
}
/**
* get all the packages that update time > lastSyncTime
* @param {Number} lastSyncTime
*/
function* getCommonSyncPackages(lastSyncTime) {
var data = yield* npmService.getAllSince(lastSyncTime);
if (!data) {
return [];
}
delete data._updated;
return Object.keys(data);
}
module.exports = function* sync() {
var syncTime = Date.now();
var info = yield* totalService.getTotalInfo();
@@ -66,7 +53,9 @@ module.exports = function* sync() {
logger.syncInfo('First time sync all packages from official registry');
packages = yield* getFirstSyncPackages(info.last_sync_module);
} else {
packages = yield* getCommonSyncPackages(info.last_sync_time - ms('10m'));
var result = yield npmService.fetchUpdatesSince(info.last_sync_time);
syncTime = result.lastModified;
packages = result.names;
}
packages = packages || [];

View File

@@ -1,6 +1,4 @@
/*!
* cnpmjs.org - sync/sync_exist.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
@@ -16,7 +14,6 @@
var debug = require('debug')('cnpmjs.org:sync:sync_exist');
var Status = require('./status');
var ms = require('humanize-ms');
var thunkify = require('thunkify-wrap');
var config = require('../config');
var npmService = require('../services/npm');
@@ -63,16 +60,10 @@ module.exports = function* sync() {
}
allPackages = pkgs;
} else {
debug('sync new module from last exist sync time: %s', info.last_sync_time);
var data = yield* npmService.getAllSince(info.last_exist_sync_time - ms('10m'));
if (!data) {
allPackages = [];
}
if (data._updated) {
syncTime = data._updated;
delete data._updated;
}
allPackages = Object.keys(data);
debug('sync new module from last exist sync time: %s', info.last_exist_sync_time);
var result = yield npmService.fetchUpdatesSince(info.last_exist_sync_time);
allPackages = result.names;
syncTime = result.lastModified;
}
var packages = intersection(existPackages, allPackages);

66
sync/sync_since.js Normal file
View File

@@ -0,0 +1,66 @@
/**!
* sync packages since by some days ago
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <m@fengmk2.com> (http://fengmk2.com)
*/
'use strict';
/**
* Module dependencies.
*/
const thunkify = require('thunkify-wrap');
const co = require('co');
const ms = require('humanize-ms');
const npmService = require('../services/npm');
const SyncModuleWorker = require('../controllers/sync_module_worker');
function* sync(sinceTimestamp) {
console.log('Fetching packages since: %s', new Date(sinceTimestamp));
var result = yield npmService.fetchAllPackagesSince(sinceTimestamp);
var packages = result.names;
packages = packages || [];
if (!packages.length) {
console.log('no packages need be sync');
process.exit(0);
}
// var news = [];
// for (var i = 0; i < packages.length; i++) {
// if (packages[i] === 'elwms') {
// news = packages.slice(i);
// break;
// }
// }
// packages = news;
console.log('lastModified: %s, lastModified package: %s, total %d packages to sync: %j',
new Date(result.lastModified), result.lastModifiedName, packages.length, packages);
var worker = new SyncModuleWorker({
username: 'sync_since',
name: packages,
noDep: true,
concurrency: 1,
syncUpstreamFirst: false,
});
worker.start();
var end = thunkify.event(worker);
yield end();
console.log('All packages sync done, successes %d, fails %d',
worker.successes.length, worker.fails.length);
process.exit(0);
}
co(function* () {
var timestamp = Date.now() - ms(process.argv[2] || '30d');
yield sync(timestamp);
}).catch(function (err) {
console.error(err.stack);
process.exit(1);
});

View File

@@ -1,11 +1,9 @@
/**!
* cnpmjs.org - test/controllers/registry/package/download.test.js
*
* Copyright(c) fengmk2 and other contributors.
* Copyright(c) cnpm and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/
'use strict';
@@ -15,36 +13,66 @@
*/
var request = require('supertest');
var mm = require('mm');
var config = require('../../../../config');
var app = require('../../../../servers/registry');
var utils = require('../../../utils');
describe('controllers/registry/package/download.test.js', function () {
describe('test/controllers/registry/package/download.test.js', function () {
before(function (done) {
var pkg = utils.getPackage('@cnpmtest/download-test-module', '1.0.0', utils.admin);
request(app.listen())
request(app)
.put('/' + pkg.name)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, done);
});
afterEach(mm.restore);
describe('GET /:name/download/:filename', function () {
it('should download a file with 200', function (done) {
request(app.listen())
request(app)
.get('/@cnpmtest/download-test-module/download/@cnpmtest/download-test-module-1.0.0.tgz')
.expect(200, done);
});
it('should alias /:name/-/:filename to /:name/download/:filename', function (done) {
request(app.listen())
request(app)
.get('/@cnpmtest/download-test-module/-/@cnpmtest/download-test-module-1.0.0.tgz')
.expect(200, done);
});
it('should 404 when package not exists', function (done) {
request(app.listen())
request(app)
.get('/@cnpmtest/download-test-module-not-exists/download/@cnpmtest/download-test-module-not-exists-1.0.0.tgz')
.expect(404, done);
});
describe('nfs.url is function', function() {
it('should work with nfs.url is generatorFunction', function(done) {
mm(config.nfs, 'url', function*(key) {
return 'http://foo.test.com' + key;
});
mm(config, 'downloadRedirectToNFS', true);
request(app)
.get('/@cnpmtest/download-test-module/-/@cnpmtest/download-test-module-1.0.0.tgz')
.expect('Location', 'http://foo.test.com/@cnpmtest/download-test-module/-/@cnpmtest/download-test-module-1.0.0.tgz')
.expect(302, done);
});
it('should work with nfs.url is function', function(done) {
mm(config.nfs, 'url', function(key) {
return 'http://foo.test.com' + key;
});
mm(config, 'downloadRedirectToNFS', true);
request(app)
.get('/@cnpmtest/download-test-module/-/@cnpmtest/download-test-module-1.0.0.tgz')
.expect('Location', 'http://foo.test.com/@cnpmtest/download-test-module/-/@cnpmtest/download-test-module-1.0.0.tgz')
.expect(302, done);
});
});
});
});

View File

@@ -0,0 +1,87 @@
/**!
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/
'use strict';
/**
* Module dependencies.
*/
var request = require('supertest');
var mm = require('mm');
var should = require('should');
var app = require('../../../../servers/registry');
var utils = require('../../../utils');
describe('test/controllers/registry/package/list_by_user.test.js', function () {
var user = 'cnpmjstest_list_by_user';
var userauth = 'Basic ' + new Buffer(user + ':' + user).toString('base64');
afterEach(mm.restore);
before(function (done) {
var pkg = utils.getPackage('@cnpmtest/list_by_user_module1', '1.0.1', user);
request(app)
.put('/' + pkg.name)
.set('authorization', userauth)
.send(pkg)
.expect(201, function (err) {
should.not.exist(err);
var pkg2 = utils.getPackage('@cnpmtest/list_by_user_module2', '2.0.0', user);
request(app)
.put('/' + pkg2.name)
.set('authorization', userauth)
.send(pkg2)
.expect(201, done);
});
});
describe('GET /-/users/:user/packages', function () {
it('should get 200', function (done) {
var url = '/-/users/' + user + '/packages';
request(app)
.get(url)
.expect(function(res) {
var data = res.body;
data.user.name.should.equal(user);
var map = {};
data.packages.forEach(function(pkg) {
map[pkg.name] = pkg;
});
map['@cnpmtest/list_by_user_module1'].should.be.an.Object;
map['@cnpmtest/list_by_user_module1'].should.eql({
name: '@cnpmtest/list_by_user_module1',
description: '',
version: '1.0.1',
});
map['@cnpmtest/list_by_user_module2'].should.be.an.Object;
map['@cnpmtest/list_by_user_module2'].should.eql({
name: '@cnpmtest/list_by_user_module2',
description: '',
version: '2.0.0',
});
})
.expect(200, done);
});
it('should get empty packages list when user not exists', function (done) {
var url = '/-/users/not-exist-username/packages';
request(app)
.get(url)
.expect({
user: {
name: 'not-exist-username',
},
packages: [],
})
.expect(200, done);
});
});
});

View File

@@ -5,7 +5,7 @@
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/
'use strict';
@@ -23,7 +23,7 @@ var config = require('../../../../config');
var packageService = require('../../../../services/package');
var nfs = require('../../../../common/nfs');
describe('controllers/registry/package/remove.test.js', function () {
describe('test/controllers/registry/package/remove.test.js', function () {
afterEach(mm.restore);
before(function (done) {
@@ -74,7 +74,7 @@ describe('controllers/registry/package/remove.test.js', function () {
.set('authorization', utils.otherUserAuth)
.expect({
error: 'invalid scope',
reason: 'scope @cnpm-not-exists not match legal scopes: @cnpm, @cnpmtest'
reason: 'scope @cnpm-not-exists not match legal scopes: @cnpm, @cnpmtest, @cnpm-test'
})
.expect(400, done);
});
@@ -85,7 +85,7 @@ describe('controllers/registry/package/remove.test.js', function () {
.set('authorization', utils.otherUserAuth)
.expect({
error: 'no_perms',
reason: 'only allow publish with @cnpm, @cnpmtest scope(s)'
reason: 'only allow publish with @cnpm, @cnpmtest, @cnpm-test scope(s)'
})
.expect(403, done);
});

View File

@@ -51,7 +51,7 @@ describe('controllers/registry/user/show.test.js, GET /-/user/org.couchdb.user:n
.expect(200, function (err, res) {
should.not.exist(err);
res.body.name.should.equal('fengmk2');
res.body.github.should.equal('fengmk2');
// res.body.github.should.equal('fengmk2');
res.body._cnpm_meta.should.have.keys('id', 'npm_user', 'custom_user',
'gmt_modified', 'gmt_create');
res.body._cnpm_meta.npm_user.should.equal(true);

View File

@@ -129,7 +129,7 @@ describe('controllers/sync.test.js', function () {
it('should sync scope package not found', function (done) {
request(webApp.listen())
.put('/sync/@cnpm/not-exists-package')
.expect(404, done);
.expect(201, done);
});
});
});

View File

@@ -5,7 +5,7 @@
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/
'use strict';
@@ -17,6 +17,7 @@
var mm = require('mm');
var thunkify = require('thunkify-wrap');
var request = require('supertest');
var urllib = require('urllib');
var config = require('../../config');
var SyncModuleWorker = require('../../controllers/sync_module_worker');
var logService = require('../../services/module_log');
@@ -24,7 +25,7 @@ var packageService = require('../../services/package');
var utils = require('../utils');
var app = require('../../servers/registry');
describe('controllers/sync_module_worker.test.js', function () {
describe('test/controllers/sync_module_worker.test.js', function () {
afterEach(mm.restore);
beforeEach(function () {
@@ -53,9 +54,9 @@ describe('controllers/sync_module_worker.test.js', function () {
yield end();
});
it('should not sync scoped package', function* () {
it('should not sync private scoped package', function* () {
var worker = new SyncModuleWorker({
name: '@scoped/google',
name: '@cnpmtest/google',
username: 'fengmk2',
});
worker.start();
@@ -63,6 +64,44 @@ describe('controllers/sync_module_worker.test.js', function () {
yield end();
});
it('should sync public scoped package', function* () {
mm(config, 'sourceNpmRegistry', 'https://registry.npmjs.org');
var worker = new SyncModuleWorker({
name: '@sindresorhus/df',
username: 'fengmk2',
});
worker.start();
var end = thunkify.event(worker, 'end');
yield end();
// sync again
var worker = new SyncModuleWorker({
name: '@sindresorhus/df',
username: 'fengmk2',
});
worker.start();
var end = thunkify.event(worker, 'end');
yield end();
var tgzUrl;
function checkResult() {
return function (done) {
request(app.listen())
.get('/@sindresorhus/df')
.expect(function (res) {
var latest = res.body.versions[res.body['dist-tags']['latest']];
tgzUrl = latest.dist.tarball;
})
.expect(200, done);
};
}
yield checkResult();
var r = yield urllib.request(tgzUrl);
r.status.should.equal(200);
});
it('should start a sync worker and dont sync deps', function* () {
var log = yield* logService.create({
name: 'byte',

View File

@@ -5,7 +5,7 @@
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/
'use strict';
@@ -35,7 +35,7 @@ describe('controllers/web/badge.test.js', function () {
should.not.exists(err);
request(app)
.get('/badge/v/@cnpmtest/badge-test-module.svg?style=flat-square')
.expect('Location', 'https://img.shields.io/badge/cnpm-1.0.1-blue.svg?style=flat-square')
.expect('Location', 'https://dn-img-shields-io.qbox.me/badge/cnpm-1.0.1-blue.svg?style=flat-square')
.expect(302, done);
});
});
@@ -50,7 +50,22 @@ describe('controllers/web/badge.test.js', function () {
should.not.exists(err);
request(app)
.get('/badge/v/@cnpmtest/badge-test-module.svg?style=flat-square&tag=v2')
.expect('Location', 'https://img.shields.io/badge/cnpm-2.0.1-blue.svg?style=flat-square')
.expect('Location', 'https://dn-img-shields-io.qbox.me/badge/cnpm-2.0.1-blue.svg?style=flat-square')
.expect(302, done);
});
});
it('should support custom subject', function (done) {
var pkg = utils.getPackage('@cnpmtest/badge-test-module', '3.0.1', utils.admin, 'v3');
request(registry)
.put('/' + pkg.name)
.set('authorization', utils.adminAuth)
.send(pkg)
.end(function (err) {
should.not.exists(err);
request(app)
.get('/badge/v/@cnpmtest/badge-test-module.svg?style=flat-square&tag=v3&subject=ant-design')
.expect('Location', 'https://dn-img-shields-io.qbox.me/badge/ant--design-3.0.1-blue.svg?style=flat-square')
.expect(302, done);
});
});
@@ -65,7 +80,7 @@ describe('controllers/web/badge.test.js', function () {
should.not.exists(err);
request(app)
.get('/badge/v/@cnpmtest/badge-test-module.svg?style=flat-square')
.expect('Location', 'https://img.shields.io/badge/cnpm-1.0.0--beta1-blue.svg?style=flat-square')
.expect('Location', 'https://dn-img-shields-io.qbox.me/badge/cnpm-1.0.0--beta1-blue.svg?style=flat-square')
.expect(302, done);
});
});
@@ -80,7 +95,7 @@ describe('controllers/web/badge.test.js', function () {
should.not.exists(err);
request(app)
.get('/badge/v/@cnpmtest/badge-test-module.svg?style=flat-square')
.expect('Location', 'https://img.shields.io/badge/cnpm-0.1.0-green.svg?style=flat-square')
.expect('Location', 'https://dn-img-shields-io.qbox.me/badge/cnpm-0.1.0-green.svg?style=flat-square')
.expect(302, done);
});
});
@@ -95,7 +110,7 @@ describe('controllers/web/badge.test.js', function () {
should.not.exists(err);
request(app)
.get('/badge/v/@cnpmtest/badge-test-module.svg?style=flat-square')
.expect('Location', 'https://img.shields.io/badge/cnpm-0.0.0-red.svg?style=flat-square')
.expect('Location', 'https://dn-img-shields-io.qbox.me/badge/cnpm-0.0.0-red.svg?style=flat-square')
.expect(302, done);
});
});
@@ -103,8 +118,25 @@ describe('controllers/web/badge.test.js', function () {
it('should show invalid when package not exists', function (done) {
request(app)
.get('/badge/v/@cnpmtest/badge-test-module-not-exists.svg?style=flat')
.expect('Location', 'https://img.shields.io/badge/cnpm-invalid-lightgrey.svg?style=flat')
.expect('Location', 'https://dn-img-shields-io.qbox.me/badge/cnpm-invalid-lightgrey.svg?style=flat')
.expect(302, done);
});
});
describe('GET /badge/d/:name.svg', function () {
it('should show downloads count', function (done) {
var pkg = utils.getPackage('@cnpmtest/badge-download-module', '1.0.1', utils.admin);
request(registry)
.put('/' + pkg.name)
.set('authorization', utils.adminAuth)
.send(pkg)
.end(function (err) {
should.not.exists(err);
request(app)
.get('/badge/d/@cnpmtest/badge-download-module.svg?style=flat-square')
.expect('Location', 'https://dn-img-shields-io.qbox.me/badge/downloads-0-brightgreen.svg?style=flat-square')
.expect(302, done);
});
});
});
});

View File

@@ -115,13 +115,13 @@ describe('controllers/web/package/show.test.js', function () {
describe('unpublished package', function () {
before(function (done) {
utils.sync('tnpm', done);
utils.sync('tfs', done);
});
it('should display unpublished info', function (done) {
mm(config, 'syncModel', 'all');
request(app)
.get('/package/tnpm')
.get('/package/tfs')
.expect(200)
.expect(/This package has been unpublished\./, done);
});

View File

@@ -38,6 +38,7 @@ var usernames = [
'cnpmjstest10', // admin
'cnpmjstestAdmin2', // other admin
'cnpmjstestAdmin3', // other admin
'cnpmjstest_list_by_user',
];
var count = usernames.length;

View File

@@ -6,7 +6,7 @@
*
* Authors:
* dead_horse <dead_horse@qq.com>
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/
'use strict';
@@ -17,7 +17,7 @@
var common = require('../../lib/common');
describe('lib/common.test.js', function () {
describe('test/lib/common.test.js', function () {
describe('isAdmin()', function () {
it('should admin is admin', function () {
common.isAdmin('admin').should.equal(true);
@@ -26,4 +26,13 @@ describe('lib/common.test.js', function () {
common.isAdmin('toString').should.equal(false);
});
});
describe('getCDNKey()', function () {
it('should auto fix scope filename', function () {
common.getCDNKey('foo', 'foo-1.0.0.tgz').should.equal('/foo/-/foo-1.0.0.tgz');
common.getCDNKey('@bar/foo', 'foo-1.0.0.tgz').should.equal('/@bar/foo/-/@bar/foo-1.0.0.tgz');
common.getCDNKey('@bar/foo', '@bar/foo-1.0.0.tgz').should.equal('/@bar/foo/-/@bar/foo-1.0.0.tgz');
common.getCDNKey('@bar/foo', '@bar1/foo-1.0.0.tgz').should.equal('/@bar/foo/-/@bar1/foo-1.0.0.tgz');
});
});
});

View File

@@ -9,7 +9,7 @@ module.exports = {
node: {
version: '>= 0.11.13',
title: 'node: >=0.11.13',
badgeURL: 'https://img.shields.io/badge/node-%3E%3D%200.11.13-red.svg?style=flat-square',
badgeURL: 'https://dn-img-shields-io.qbox.me/badge/node-%3E%3D%200.11.13-red.svg?style=flat-square',
}
},
_publish_on_cnpm: false,

View File

@@ -1,6 +1,15 @@
module.exports = {
package: {
name: 'inch-plugin-socket-behaviour-desktop',
description: description()
},
__requires: ['./default']
};
function description() {
var s = ''
for (var i = 0; i < 10; i++) {
s += 'description: Koa web app framework'
}
return s
}

View File

@@ -24,13 +24,14 @@ describe('services/common.test.js', function () {
describe('isPrivatePackage()', function () {
it('should detect prviate package', function* () {
mm(config, 'privatePackages', ['some-private-package', 'foobar2']);
(yield* common.isPrivatePackage('@cnpm/ooxx')).should.equal(true);
(yield* common.isPrivatePackage('@cnpm/some-private-package')).should.equal(true);
(yield* common.isPrivatePackage('some-private-package')).should.equal(true);
(yield* common.isPrivatePackage('foobar2')).should.equal(true);
common.isPrivatePackage('@cnpm/ooxx').should.equal(true);
common.isPrivatePackage('@cnpm/some-private-package').should.equal(true);
common.isPrivatePackage('some-private-package').should.equal(true);
common.isPrivatePackage('foobar2').should.equal(true);
(yield* common.isPrivatePackage('foobar')).should.equal(false);
(yield* common.isPrivatePackage('pedding-2')).should.equal(false);
common.isPrivatePackage('foobar').should.equal(false);
common.isPrivatePackage('pedding-2').should.equal(false);
common.isPrivatePackage('@public/some-package').should.equal(false);
});
});
});

View File

@@ -98,9 +98,9 @@ describe('services/default_user_service.test.js', function () {
login: 'fengmk2',
email: 'fengmk2@gmail.com',
name: 'Yuan Feng',
html_url: 'http://fengmk2.github.com',
html_url: 'http://cnpmjs.org/~fengmk2',
avatar_url: 'https://secure.gravatar.com/avatar/95b9d41231617a05ced5604d242c9670?s=50&d=retro',
im_url: 'https://twitter.com/fengmk2',
im_url: '',
site_admin: true,
scopes: ['@cnpm', '@cnpmtest'],
});

View File

@@ -16,7 +16,7 @@
var DownloadTotal = require('../../services/download_total');
describe('services/download_total.test.js', function () {
describe('test/services/download_total.test.js', function () {
describe('plusModuleTotal()', function () {
it('should plus one module download count', function* () {
var data = {
@@ -89,4 +89,25 @@ describe('services/download_total.test.js', function () {
rows[2].count.should.equal(3);
});
});
describe('getTotalByName()', function () {
it('should get total downloads', function* () {
var data = {
date: '2014-10-21',
name: 'getTotalByName-module',
count: 1000
};
yield* DownloadTotal.plusModuleTotal(data);
data = {
date: '2015-10-22',
name: 'getTotalByName-module',
count: 2
};
yield* DownloadTotal.plusModuleTotal(data);
var count = yield DownloadTotal.getTotalByName('getTotalByName-module');
count.should.equal(1002);
});
});
});

View File

@@ -55,7 +55,8 @@ describe('services/npm.test.js', function () {
throw new Error('should not run this');
} catch (err) {
err.name.should.equal('NPMServerError');
err.message.should.equal('Status 500, ' + fs.readFileSync(path.join(fixtures, '500.txt'), 'utf8'));
err.message.should.containEql('Status 500, ' +
fs.readFileSync(path.join(fixtures, '500.txt'), 'utf8'));
}
});

View File

@@ -5,7 +5,7 @@
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/
'use strict';
@@ -19,7 +19,7 @@ var sleep = require('co-sleep');
var Package = require('../../services/package');
var utils = require('../utils');
describe('services/package.test.js', function () {
describe('test/services/package.test.js', function () {
function* createModule(name, version, user, tag) {
var sourcePackage = {
version: version,
@@ -124,7 +124,7 @@ describe('services/package.test.js', function () {
var mods = yield* Package.listPublicModulesByUser('listPublicModuleNamesByUser-user');
mods.should.length(3);
mods.forEach(function (mod) {
mod.toJSON().should.have.keys('name', 'description');
mod.toJSON().should.have.keys('name', 'description', 'version');
mod.name.should.containEql('listPublicModuleNamesByUser-module');
});
});
@@ -160,12 +160,11 @@ describe('services/package.test.js', function () {
});
it('should work', function* () {
yield* createModule('@cnpm-test-scope1/test-listPrivateModules-module-1', '1.0.0');
yield* createModule('@cnpm-test-scope1/test-listPrivateModules-module-2', '1.0.0');
var modules = yield* Package.listPrivateModulesByScope('@cnpm-test-scope1');
// console.log(modules[0].toJSON())
yield* createModule('@cnpm-test/test-listPrivateModules-module-1', '1.0.0');
yield* createModule('@cnpm-test/test-listPrivateModules-module-2', '1.0.0');
var modules = yield* Package.listPrivateModulesByScope('@cnpm-test');
modules.should.length(2);
modules[0].name.should.containEql('@cnpm-test-scope1/test-listPrivateModules-module-');
modules[0].name.should.containEql('@cnpm-test/test-listPrivateModules-module-');
});
});

View File

@@ -21,7 +21,7 @@ var npmSerivce = require('../../services/npm');
var totalService = require('../../services/total');
var packageService = require('../../services/package');
describe('sync/sync_all.test.js', function () {
describe('test/sync/sync_all.test.js', function () {
beforeEach(function () {
mm(config, 'syncModel', 'all');
});
@@ -48,5 +48,31 @@ describe('sync/sync_all.test.js', function () {
data.successes.should.eql(['mk2testmodule']);
mm.restore();
});
it('should sync with array data format ok', function* () {
mm.data(npmSerivce, 'getAllSince', [
{
name: 'mk2testmodule',
versions: {
'0.0.2': 'latest'
}
},
{
name: 'mk2testmodule1',
time: {
modified: '2015-09-05T07:31:35.734Z',
},
versions: {
'0.0.2': 'latest'
}
},
]);
mm.data(npmSerivce, 'getShort', ['mk2testmodule']);
mm.data(totalService, 'getTotalInfo', {last_sync_time: Date.now()});
mm.data(packageService, 'listAllPublicModuleNames', [ 'mk2testmodule' ]);
var data = yield sync;
data.successes.should.eql(['mk2testmodule']);
mm.restore();
});
});
});

View File

@@ -21,7 +21,7 @@ var npmService = require('../../services/npm');
var totalService = require('../../services/total');
var utils = require('../utils');
describe('sync/sync_exist.test.js', function () {
describe('test/sync/sync_exist.test.js', function () {
beforeEach(function () {
mm(config, 'syncModel', 'all');
});
@@ -43,15 +43,28 @@ describe('sync/sync_exist.test.js', function () {
it('should sync common ok', function *() {
mm.data(npmService, 'getAllSince', {
_updated: Date.now(),
'byte': {},
byte: {},
});
mm.data(totalService, 'getTotalInfo', {last_exist_sync_time: Date.now()});
var data = yield* sync();
data.successes.should.eql(['byte']);
mm.data(npmService, 'getAllSince', {
_updated: Date.now(),
});
mm.data(npmService, 'getAllSince', []);
var data = yield* sync();
data.successes.should.eql([]);
});
it('should sync with array format data', function *() {
mm.data(npmService, 'getAllSince', [
{
name: 'byte',
}
]);
mm.data(totalService, 'getTotalInfo', {last_exist_sync_time: Date.now()});
var data = yield* sync();
data.successes.should.eql(['byte']);
mm.data(npmService, 'getAllSince', []);
var data = yield* sync();
data.successes.should.eql([]);
});

View File

@@ -1,11 +1,9 @@
/**!
* cnpmjs.org - test/utils.js
*
* Copyright(c) fengmk2 and other contributors.
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/
'use strict';

View File

@@ -8,34 +8,15 @@
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="shortcut icon" href="/favicon.png">
<!-- Bootstrap -->
<link href="//dn-staticfile.qbox.me/twitter-bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet" media="screen">
<!-- Ant design -->
<link href="/css/antd-0.9.1.min.css" rel="stylesheet" media="screen">
<link href="/css/main.css" rel="stylesheet" media="screen">
<link href="/css/yue.css" rel="stylesheet" media="screen">
<link href="/css/github.css" rel="stylesheet" media="screen">
<link href="/css/github-markdown.css" rel="stylesheet" media="screen">
<!-- JavaScript plugins (requires jQuery) -->
<script src="//dn-staticfile.qbox.me/jquery/2.0.3/jquery.min.js"></script>
<link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="CNPM" />
<style>
#tagline{display:inline;}
/*#wrapper{padding:5% 8%;min-width:480px;}*/
#tagline{font:0.8em serif;color:#888;display:inline-block;margin:0.1em 0 1.2em;}
#fork{position:fixed;top:0;right:0;_position:absolute;}
.bottom{text-align: center;}
#content-header {margin: 20px 0 50px 0;}
.user .avatar {
width: 32px;
height: 32px;
margin-bottom: 3px;
}
table.downloads td.count {
text-align: right;
}
.nav-tabs{margin:20px 0;}
.nav-cont{display:none;}
.nav-cont.active{display:block;}
</style>
<script>
$(function () {
$('#search-input').keypress(function (e) {
@@ -62,15 +43,17 @@
</head>
<body>
<div class="container">
<header>
<header id="header">
<div class="logo">
<a href="/"><img src="{{logoURL}}"></a>
{{adBanner}}
</div>
<input type="text" id="search-input" class="form-control" placeholder="Search Packages">
<input type="text" id="search-input" class="ant-input ant-input-lg" placeholder="Search Packages">
</header>
<%- locals.body %>
<div class="yue">
<%- locals.body %>
</div>
<div class="bottom">
<hr/>
<p>
@@ -78,8 +61,6 @@
</p>
</div>
</div>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="//dn-staticfile.qbox.me/twitter-bootstrap/3.2.0/js/bootstrap.min.js"></script>
<!-- Enable responsive features in IE8 with Respond.js (https://github.com/scottjehl/Respond) -->
<script src="//dn-staticfile.qbox.me/respond.js/1.2.0/respond.min.js"></script>
@@ -91,7 +72,6 @@
$(function () {
$('pre code').parent().addClass('prettyprint');
prettyPrint();
$('table').addClass('table table-bordered table-striped');
});
</script>
</body>

View File

@@ -4,7 +4,7 @@
<% if (package.deprecated) { %>
<div class="pack-name deprecated"><%= package.name %></div>
<% if (package.description) {%>
<div class="pack-description deprecated"><%= package.description %></div>
<p class="pack-description deprecated"><%= package.description %></p>
<% } %>
<div class="deprecated-content" title="<%= package.deprecated %>"><%= package.deprecated %></div>
<% } else { %>
@@ -23,34 +23,34 @@
<% }
}%>
</div>
<% if (package.fromNow) { %>
<div class="pack-lastup">
Last updated <%= package.fromNow %>
<% if (package.lastPublishedUser) {
var name = package.lastPublishedUser.name;
%>
by <a href="/~<%= name %>"><%= name %></a>
<% } %>
.
</div>
<% } %>
<div class="pack-repo">
<% if (package.fromNow) { %>
<div class="pack-lastup">
Last updated <%= package.fromNow %>
<% if (package.lastPublishedUser) {
var name = package.lastPublishedUser.name;
%>
by <a href="/~<%= name %>"><%= name %></a>
<% } %>
.
</div>
<% } %>
<% if (package.license) { %>
<a href="<%= package.license.url %>" target="_blank"><%= package.license.name %></a>
<% } %>
<% if (package.repository) { %>
<% if (package.license) {%> | <% } %>
<% if (package.license) {%> &middot; <% } %>
<a href="<%= package.repository.weburl %>" target="_blank">Repository</a>
<% } %>
<% if (package.bugs.url) { %>
<% if (package.license || package.repository) {%> | <% } %>
<% if (package.license || package.repository) {%> &middot; <% } %>
<a href="<%= package.bugs.url %>" target="_blank">Bugs</a>
<% } %>
<% if (package.license || package.repository || package.bugs.url) {%> | <% } %>
<% if (package.license || package.repository || package.bugs.url) {%> &middot; <% } %>
<% if (!package._publish_on_cnpm) { %>
<a href="https://www.npmjs.org/package/<%= package.name %>" target="_blank">Original npm</a>
@@ -71,106 +71,117 @@
</div>
</div>
<div class="pack-details cols-box">
<div class="pack-dep cols-box-item">
<div class="cols-box-title">
<%
var deps = Object.keys(package.dependencies || {})
var l = deps.length
var m = 8
%>
Dependencies<%= ' (' + l + ')' %>
<% if (l > m) { %>
<a class="more-item" href="<%= package.registryUrl %>" target="_blank">+<%= l-m %> more</a>
<% } %>
</div>
<% if (l) { %>
<ul>
<% if (l > m) deps = deps.slice(0, m)
deps.forEach(function(pkg, i) {
%>
<li>
<a class="pack-dep-name" target="_blank" href="/package/<%= pkg %>"><%= pkg %></a>
<span class="pack-dep-ver"><%= package.dependencies[pkg]%></span>
</li>
<% }) %>
</ul>
<% } else { %>
None
<% } %>
<div class="row">
<div class="pack-readme col-17">
<section id="readme" class="markdown-body">
<%- package.readme || 'No README was found.' %>
</section>
</div>
<div class="pack-maintainers cols-box-item">
<div class="cols-box-title">
<%
var maintainers = package.maintainers || [];
var l = maintainers.length
var m = 6
%>
Maintainers <%= l > 0 ? ' (' + l + ')' : '' %>
<% if (l > m) { %>
<a class="more-item" href="<%= package.registryUrl %>" target="_blank">+<%= l-m %> more</a>
<% } %>
</div>
<div class="maintainers">
<% if (l > m) maintainers = maintainers.slice(0, m) %>
<% for (var i = 0; i < maintainers.length; i += 2) { %>
<div class="row">
<div class="pack-details cols-box col-6 col-push-1">
<div class="pack-maintainers cols-box-item">
<div class="cols-box-title">
<%
var maintainers = package.maintainers || [];
var l = maintainers.length
%>
Maintainers <%= l > 0 ? ' (' + l + ')' : '' %>
</div>
<div class="maintainers">
<% for (var i = 0; i < maintainers.length; i ++) { %>
<div class="maintainer">
<a class="username" href="/~<%= maintainers[i].name %>">
<img src="<%= maintainers[i].gravatar %>" class="avatar">
<%= maintainers[i].name %>
</a>
</div>
<% if (maintainers[i + 1]) { %>
<div class="maintainer">
<a class="username" href="/~<%= maintainers[i + 1].name %>">
<img src="<%= maintainers[i + 1].gravatar %>" class="avatar">
<%= maintainers[i + 1].name %>
</a>
</div>
<% } %>
<% } %>
</div>
</div>
<div class="pack-downloads cols-box-item">
<div class="cols-box-title">
Downloads
</div>
<div class="downloads">
<div class="row">
Today
<span class="pack-download-num"><%= download.today %></span>
</div>
<div class="row">
This Week
<span class="pack-download-num"><%= download.thisweek %></span>
</div>
<div class="row">
This Month
<span class="pack-download-num"><%= download.thismonth %></span>
</div>
<div class="row">
Last Day
<span class="pack-download-num"><%= download.lastday %></span>
</div>
<div class="row">
Last Week
<span class="pack-download-num"><%= download.lastweek %></span>
</div>
<div class="row">
Last Month
<span class="pack-download-num"><%= download.lastmonth %></span>
</div>
</div>
</div>
<div class="pack-dep cols-box-item">
<div class="cols-box-title">
<%
var deps = Object.keys(package.dependencies || {})
var l = deps.length
%>
Dependencies<%= ' (' + l + ')' %>
</div>
<% if (l) { %>
<ul>
<%
deps.forEach(function(pkg, i) {
%>
<li>
<a class="pack-dep-name" target="_blank" href="/package/<%= pkg %>"><%= pkg %></a>
<span class="pack-dep-ver"><%= package.dependencies[pkg]%></span>
</li>
<% }) %>
</ul>
<% } else { %>
None
<% } %>
</div>
</div>
<div class="pack-dep cols-box-item">
<div class="cols-box-title">
<%
var deps = Object.keys(package.devDependencies || {})
var l = deps.length
%>
Dev Dependencies<%= ' (' + l + ')' %>
<div class="pack-downloads cols-box-item">
<div class="cols-box-title">
Downloads
</div>
<div class="downloads">
<div class="row">
Today
<span class="pack-download-num"><%= download.today %></span>
</div>
<div class="row">
This Week
<span class="pack-download-num"><%= download.thisweek %></span>
</div>
<div class="row">
This Month
<span class="pack-download-num"><%= download.thismonth %></span>
</div>
<div class="row">
Last Day
<span class="pack-download-num"><%= download.lastday %></span>
</div>
<div class="row">
Last Week
<span class="pack-download-num"><%= download.lastweek %></span>
</div>
<div class="row">
Last Month
<span class="pack-download-num"><%= download.lastmonth %></span>
</div>
</div>
<% if (l) { %>
<ul>
<%
deps.forEach(function(pkg, i) {
%>
<li>
<a class="pack-dep-name" target="_blank" href="/package/<%= pkg %>"><%= pkg %></a>
<span class="pack-dep-ver"><%= package.devDependencies[pkg]%></span>
</li>
<% }) %>
</ul>
<% } else { %>
None
<% } %>
</div>
</div>
</div>
<div class="pack-readme">
<div class="title">README</div>
<section id="readme" class="markdown-body">
<%- package.readme %>
</section>
</div>

View File

@@ -7,65 +7,69 @@
</h1>
<p class="description">This package has been unpublished.</p>
<ul class="nav nav-tabs">
<li class="active"><a href="#meta">package.json</a></li>
<!-- package.json? -->
<ul class="ant-menu ant-menu-horizontal ant-menu-root unstyled mb15">
<li class="ant-menu-item-selected ant-menu-item unstyled">
<span>package.json</span>
</li>
</ul>
<table class="metadata active">
<% if (package.unpublished.description) { %>
<tr>
<th>Description</th>
<td>
<%= package.unpublished.description %>
</td>
</tr>
<% } %>
<div class="ant-table">
<table class="metadata active">
<% if (package.unpublished.description) { %>
<tr>
<th>Description</th>
<td>
<%= package.unpublished.description %>
</td>
</tr>
<% } %>
<tr>
<th>Unpublished By</th>
<td>
<span class="user">
<a class="username" href="/~<%= package.unpublished.name %>">
<%= package.unpublished.name %>
</a>
</span>
</td>
</tr>
<tr>
<th>Unpublished By</th>
<td>
<span class="user">
<a class="username" href="/~<%= package.unpublished.name %>">
<%= package.unpublished.name %>
</a>
</span>
</td>
</tr>
<tr>
<th>Unpublished On</th>
<td>
<%= package.unpublished.time %>
</td>
</tr>
<tr>
<th>Unpublished On</th>
<td>
<%= package.unpublished.time %>
</td>
</tr>
<% if (package.unpublished.maintainers) { %>
<tr>
<th>Maintainers</th>
<td>
<% package.unpublished.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>
<% } %>
<% if (package.unpublished.maintainers) { %>
<tr>
<th>Maintainers</th>
<td>
<% package.unpublished.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>
<% } %>
<% if (package.unpublished.versions) { %>
<tr>
<th>Versions (<%= package.unpublished.versions.length %>)</th>
<td>
<%= package.unpublished.versions.reverse().slice(0, 20).join(', ') %> ...
</td>
</tr>
<% } %>
<% if (package.unpublished.versions) { %>
<tr>
<th>Versions (<%= package.unpublished.versions.length %>)</th>
<td>
<%= package.unpublished.versions.reverse().slice(0, 20).join(', ') %> ...
</td>
</tr>
<% } %>
</table>
</table>
</div>
</div>

View File

@@ -1,12 +1,5 @@
<style>
#profile .package {
padding: 10px;
font-size: 18px;
border-bottom: 1px solid #ddd;
}
</style>
<div id="profile">
<h1>
<h2>
Packages by
<% if (user.json.homepage) { %>
<span class="user">
@@ -20,49 +13,57 @@
<% } else { %>
<span style="color:#09f;"><%= user.name %></span>
<% } %>
<% if (user.json.fullname) { %>
<small>(<%= user.json.fullname %>)</small>
<% } %>
<% if (user.email) { %>
<small>(<a href="mailto:<%= user.email %>"><%= user.email %></a>)</small>
<% } %>
<% if (user.email) { %>
<small><<a href="mailto:<%= user.email %>"><%= user.email %></a>></small>
<% } %>
<% if (user.json.fullname) { %>
<small>(<%= user.json.fullname %>)</small>
<% } %>
<small>(<a href="/sync/user:<%= user.name %>" target="_blank">SYNC</a> profile)</small>
</h1>
<table class="downloads">
<tbody>
<tr>
<th>Name</th>
<th>IM</th>
<th>Last modified</th>
<th>Is admin</th>
<th>Publish scopes</th>
</tr>
<tr>
<td>@<%= user.json.fullname || user.name %></td>
<td>
<% if (user.json.im) { %>
<a href="<%= user.json.im %>" target="_blank"><span class="glyphicon glyphicon-comment"></span></a>
<% } %>
</td>
<td><%- lastModified %></td>
<td><%- isAdmin %></td>
<td>
<%- scopes.map(function (scope) {
return '<a href="/browse/keyword/' + scope + '" target="_blank">' + scope + '</a>';
}).join(' , ') %>
</td>
</tr>
</tbody>
</table>
<small>[<a href="/sync/user:<%= user.name %>" target="_blank">SYNC</a> profile]</small>
</h2>
<div class="ant-table">
<table class="downloads">
<tbody>
<tr>
<th>Name</th>
<th>IM</th>
<th>Last modified</th>
<th>Is admin</th>
<th>Publish scopes</th>
</tr>
<tr>
<td>@<%= user.json.fullname || user.name %></td>
<td>
<% if (user.json.im) { %>
<a href="<%= user.json.im %>" target="_blank"><span class="glyphicon glyphicon-comment"></span></a>
<% } %>
</td>
<td><%- lastModified %></td>
<td><%- isAdmin %></td>
<td>
<%- scopes.map(function (scope) {
return '<a href="/browse/keyword/' + scope + '" target="_blank">' + scope + '</a>';
}).join(' , ') %>
</td>
</tr>
</tbody>
</table>
</div>
<hr />
<% if (!packages.length) { %>
<div class="alert alert-warning">Can not found any package by <%= user.name %>.</div>
<% } %>
<% for (var i = 0; i < packages.length; i++) { %>
<div class="package">
<a href="/package/<%= packages[i].name %>" class="package-name"><%= packages[i].name %></a>
<span class="package-description"><%= packages[i].description %></span>
</div>
<% for (var i = 0; i < packages.length; i++) {
var package = packages[i];
%>
<ul>
<li class="package">
<a href="/package/<%= packages[i].name %>" class="package-name"><%= package.name %></a>
<span class="package-description">(latest: <%= package.version %>) <%= package.description %></span>
</li>
</ul>
<% } %>
</div>

View File

@@ -1,42 +1,30 @@
<style>
#search .package {
padding: 10px;
font-size: 18px;
border-bottom: 1px solid #ddd;
}
#search .alert a {
font-size: 20px;
}
#search .package.match {
font-size: 25px;
font-weight: bolder;
}
</style>
<div id="search">
<div id="search" class="row">
<% if (!packages.length) { %>
<div class="alert alert-warning">
<div class="ant-alert ant-alert-warn">
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 (!match) { %>
<div class="alert alert-info">
<div class="ant-alert ant-alert-info">
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 class="col-12">
<% if (packages.length) { %>
<h1>
<h2>
Packages match "<span style="color: #09f;"><%= keyword %></span>"
</h1>
<hr />
</h2>
<% if (match) { %>
<div class="package match">
<a href="/package/<%= match.name %>" class="package-name"><%= match.name %></a>
<span class="package-description"><%= match.description %></span>
</div>
<ul>
<li class="package match">
<a href="/package/<%= match.name %>" class="package-name"><%= match.name %></a>
<span class="package-description"><%= match.description %></span>
</li>
</ul>
<% } %>
<% for (var i = 0; i < packages.length; i++) {
var item = packages[i];
@@ -44,25 +32,30 @@
continue;
}
%>
<div class="package">
<ul>
<li class="package">
<a href="/package/<%= item.name %>" class="package-name"><%= item.name %></a>
<span class="package-description"><%= item.description %></span>
</div>
</li>
</ul>
<% } %>
<% } %>
</div>
<div class="col-12">
<% if (keywords.length) { %>
<h1>
<h2>
Keywords match "<span style="color: #09f;"><%= keyword %></span>"
</h1>
<hr />
</h2>
<% for (var i = 0; i < keywords.length; i++) {
var item = keywords[i];
%>
<div class="package <%= item.name === keyword ? 'match' : '' %>">
<ul>
<li 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>
</li>
</ul>
<% } %>
<% } %>
</div>
</div>

View File

@@ -1,7 +1,7 @@
<div id="sync">
<h1>Sync <%= type %> <span style="color:#09f;"><%= name %></span></h1>
<h2>Sync <%= type %> <span style="color:#09f;"><%= name %></span></h2>
<div id="sync-notify">
<div class="alert alert-success">Sync started, please wait patiently.</div>
<div class="ant-alert ant-alert-success">Sync started, please wait patiently.</div>
</div>
<h2>Log</h2>
<pre style="min-height: 400px;" id="sync-log"></pre>
@@ -10,7 +10,11 @@
var $log = $('#sync-log');
var $notify = $('#sync-notify');
var timer;
var name = '<%= type %>:<%= name %>';
var type = '<%= type %>';
var name = '<%= name %>';
if (type !== 'package') {
name = type + ':' + name;
}
var resourceURL = '/sync/' + name;
$(function() {
var checkLogId = location.hash.match(/logid=(\d+)/);
@@ -24,7 +28,7 @@
dataType: 'json',
success: handleSyncSucess,
error: function (err) {
var alert = $('<div class="alert alert-error"></div>');
var alert = $('<div class="ant-alert ant-alert-error"></div>');
var message = 'Sync request error. ';
if (err.status === 404) {
message += name + ' not exist in official registry.';
@@ -44,7 +48,7 @@
location.hash = '#logid=' + data.logId;
return;
}
$notify.html('<div class="alert alert-error">Sync request error.</div>');
$notify.html('<div class="ant-alert ant-alert-error">Sync request error.</div>');
}
var offset = 0;

View File

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