Compare commits

...

293 Commits

Author SHA1 Message Date
fengmk2
bfbfef87d2 Release 2.13.0 2016-07-26 18:33:32 +08:00
fengmk2
ea3beceef7 feat: enable maxrequests middleware (#1003) 2016-07-26 18:32:33 +08:00
fengmk2
b0f0bc0331 Release 2.12.2 2016-07-11 12:48:36 +08:00
Yiyu He
932547af2a fix: getModuleByRange don't list all packages (#990) 2016-07-11 12:46:52 +08:00
fengmk2
79ac9fb823 fix: should show new version package count (#984) 2016-07-04 00:06:42 +08:00
fengmk2
a437eb01eb Release 2.12.1 2016-07-01 17:59:38 +08:00
fengmk2
6d921a9c46 fix: make sure chagnes stream destroy (#982)
closes #981
2016-07-01 17:58:24 +08:00
Greenkeeper
abcb81d28c chore(package): update semver to version 5.2.0 (#978)
https://greenkeeper.io/
2016-07-01 15:27:10 +08:00
fengmk2
ea54f32805 deps: use ^ instead of ~ (#976) 2016-06-27 01:01:23 +08:00
Greenkeeper
3dbf29afac chore(package): update mini-logger to version 1.1.1 (#973)
https://greenkeeper.io/
2016-06-26 13:32:53 +08:00
dead_horse
2c520d97a9 Release 2.12.0 2016-06-26 13:30:41 +08:00
fengmk2
38148b24cb fix: logger seperator should be one EOL (#972) 2016-06-26 13:28:42 +08:00
fengmk2
97895bdfa5 feat: add security check badge for public package (#971) 2016-06-26 13:18:33 +08:00
dead_horse
0c1c12680c Release 2.11.0 2016-06-25 21:06:08 +08:00
fengmk2
6485e4b019 feat: add changes stream syncer (#970)
* feat: add changes stream syncer

fix scope package missing problem

close #908

* refactor: use let instead of var
2016-06-25 21:05:00 +08:00
Greenkeeper
ee1e084455 chore(package): update pg to version 5.1.0 (#953)
https://greenkeeper.io/
2016-06-11 13:02:41 +08:00
dead_horse
5c8d679a6d Release 2.10.1 2016-06-05 23:37:44 +08:00
fengmk2
94dc13088c fix: should sync missing public scoped package on install (#946)
* fix: should sync missing public scoped package on install

closes #938

* refactor: sync all scoped packages when config.scopes is empty

* test: test on node 6
2016-06-05 23:34:10 +08:00
fengmk2
9b572d77aa docs: update npmjs.com, cnpmjs.org and npm.taobao.org relation (#945) 2016-06-05 22:14:56 +08:00
Greenkeeper
c1fa6de038 chore(package): update bytes to version 2.4.0 (#943)
https://greenkeeper.io/
2016-06-02 10:53:05 +08:00
alsotang
227e1927b4 userService (#926) 2016-05-23 17:50:29 +08:00
Greenkeeper
ea98b61ddf chore(package): update should to version 8.4.0 (#928)
https://greenkeeper.io/
2016-05-23 10:27:09 +08:00
Greenkeeper
9d1d60b764 chore(package): update humanize-ms to version 1.2.0 (#927)
https://greenkeeper.io/
2016-05-21 00:34:18 +08:00
Greenkeeper
7509d909e8 chore(package): update kcors to version 1.2.1 (#918)
https://greenkeeper.io/
2016-05-14 21:24:17 +08:00
Greenkeeper
9612f64aba chore(package): update urllib to version 2.9.0 (#898)
https://greenkeeper.io/
2016-04-21 10:38:09 +08:00
fengmk2
1b04d59539 Release 2.10.0 2016-04-15 23:45:54 +08:00
fengmk2
e6998506f3 feat: show tarball url on package page (#894)
closes #893
2016-04-15 23:44:07 +08:00
Greenkeeper
576397b2b0 chore(package): update koa-mock to version 1.6.1 (#891)
http://greenkeeper.io/
2016-04-13 16:53:16 +08:00
Greenkeeper
6c1935bc61 chore(package): update koa-mock to version 1.6.0 (#890)
http://greenkeeper.io/
2016-04-13 09:58:15 +08:00
fengmk2
1e68cfa568 Release 2.9.5 2016-04-12 11:24:10 +08:00
fengmk2
8188d9a07a fix: change logo url to a better https source
closes #887
2016-04-12 11:21:58 +08:00
Nimo Chu
d7ae8d28c1 fix: http://cnpmjs.org/package/fms pre style (#739) 2016-04-09 12:38:30 +08:00
fengmk2
afa5639752 Release 2.9.4 2016-04-09 12:34:39 +08:00
fengmk2
e0213e18a0 fix: don't sync constructor package on exists mode (#883)
closes #857
2016-04-09 12:33:52 +08:00
Greenkeeper
a74d60c7eb Update utility to version 1.7.0 🚀
http://greenkeeper.io/
2016-04-07 09:22:29 +08:00
fengmk2
fcabc49d39 chore: update sponsor link 2016-04-05 23:06:48 +08:00
fengmk2
ad0722b57b Release 2.9.3 2016-04-05 22:39:37 +08:00
fengmk2
6e11a2c8a7 fix: use better diff time to check sync status
closes #858
2016-04-05 15:43:29 +08:00
Greenkeeper
0329cfad61 Update sequelize to version 3.21.0 🚀
http://greenkeeper.io/
2016-04-04 17:19:45 +08:00
Yiyu He
ede8a3ce5f Merge pull request #876 from cnpm/greenkeeper-agentkeepalive-2.1.0
Update agentkeepalive to version 2.1.0 🚀
2016-04-02 17:49:39 +08:00
greenkeeperio-bot
cde4f2179e chore(package): update agentkeepalive to version 2.1.0
http://greenkeeper.io/
2016-04-02 11:04:29 +08:00
Yiyu He
374dc17a6b Merge pull request #875 from cnpm/greenkeeper-pg-4.5.2
Update pg to version 4.5.2 🚀
2016-03-31 01:41:35 +08:00
greenkeeperio-bot
626259c379 chore(package): update pg to version 4.5.2
http://greenkeeper.io/
2016-03-31 01:33:38 +08:00
fengmk2
36644bda67 Release 2.9.2 2016-03-29 17:56:05 +08:00
fengmk2
236e9e2fb3 Merge pull request #874 from dickeylth/master
fix: override antd for ul & ol list number & icon.
2016-03-29 17:54:06 +08:00
dickeylth
b551216517 Update main.css
override antd for ul & ol list number & icon.
2016-03-29 17:15:28 +08:00
dead_horse
c8d9d9f075 Release 2.9.1 2016-03-29 15:31:52 +08:00
Yiyu He
4d43588b10 Merge pull request #872 from cnpm/add-more-ua-info-on-syncer
refactor: add more ua info on syncer
2016-03-29 15:31:04 +08:00
fengmk2
5f71add418 refactor: add more ua info on syncer 2016-03-29 14:53:05 +08:00
fengmk2
3fcfc6306d Release 2.9.0 2016-03-26 11:16:22 +08:00
fengmk2
e78c774dbf Merge pull request #867 from cnpm/unpublish
feat: only admin can unpublish
2016-03-26 11:14:58 +08:00
dead_horse
6a54b1328f feat: only admin can unpublish 2016-03-25 17:50:24 +08:00
Yiyu He
47c8273b7d Merge pull request #864 from cnpm/greenkeeper-gravatar-1.5.0
Update gravatar to version 1.5.0 🚀
2016-03-22 11:28:49 +08:00
greenkeeperio-bot
0f01a4c076 chore(package): update gravatar to version 1.5.0
http://greenkeeper.io/
2016-03-22 08:14:10 +08:00
Yiyu He
17b43e3ae4 Merge pull request #862 from cnpm/greenkeeper-sequelize-3.20.0
Update sequelize to version 3.20.0 🚀
2016-03-22 01:35:42 +08:00
greenkeeperio-bot
7fd6b10ddf chore(package): update sequelize to version 3.20.0
http://greenkeeper.io/
2016-03-21 23:17:54 +08:00
Yiyu He
7f16050f9e Merge pull request #856 from cnpm/fix-download-count
fix: fix save download count unqiue constraint error
2016-03-14 10:46:41 +08:00
dead_horse
f57d525452 fix: fix save download count unqiue constraint error 2016-03-10 18:23:26 +08:00
fengmk2
f05046b8bd Merge pull request #854 from cnpm/greenkeeper-moment-2.12.0
Update moment to version 2.12.0 🚀
2016-03-07 22:37:30 +08:00
greenkeeperio-bot
a34aa7d72f chore(package): update moment to version 2.12.0
http://greenkeeper.io/
2016-03-07 17:28:42 +08:00
fengmk2
72646ffee2 Release 2.8.1 2016-03-07 02:11:24 +08:00
fengmk2
060fc65ffb Merge pull request #853 from cnpm/fix-sync-expired-notice
fix: only send warning email if no any sync data after 24h
2016-03-07 02:10:32 +08:00
fengmk2
4169ee9c6e fix: only send warning email if no any sync data after 24h 2016-03-07 02:02:46 +08:00
fengmk2
51f380a800 Merge pull request #852 from cnpm/greenkeeper-kcors-1.1.0
Update kcors to version 1.1.0 🚀
2016-03-07 01:50:39 +08:00
greenkeeperio-bot
c90062e7a0 chore(package): update kcors to version 1.1.0
http://greenkeeper.io/
2016-03-07 01:34:40 +08:00
Yiyu He
26007f8318 Merge pull request #848 from cnpm/greenkeeper-koa-1.2.0
Update koa to version 1.2.0 🚀
2016-03-04 12:18:11 +08:00
greenkeeperio-bot
0146ce5c30 chore(package): update koa to version 1.2.0
http://greenkeeper.io/
2016-03-04 11:34:46 +08:00
Yiyu He
87518df394 Merge pull request #846 from cnpm/greenkeeper-urllib-2.8.0
Update urllib to version 2.8.0 🚀
2016-02-28 17:29:21 +08:00
greenkeeperio-bot
e49f5474f6 chore(package): update urllib to version 2.8.0
http://greenkeeper.io/
2016-02-27 21:46:55 +08:00
fengmk2
af4401fc62 Release 2.8.0 2016-02-23 14:46:37 +08:00
fengmk2
c2d608a5cc Merge pull request #841 from cnpm/fix-range-version-for-beta-version
fix: convert `*` to latest tag
2016-02-23 14:45:39 +08:00
fengmk2
b91b160b66 fix: convert * to latest tag
closes #840
2016-02-23 14:32:59 +08:00
fengmk2
46a3df95bd Merge pull request #837 from cnpm/deps
deps: upgrade deps and remove node 2.0.0 support
2016-02-23 14:15:19 +08:00
fengmk2
d08aee047e deps: upgrade deps and remove node 2.0.0 support 2016-02-23 13:59:11 +08:00
fengmk2
406d8cf9a1 doc: update sponsors on readme 2016-02-20 02:14:00 +08:00
fengmk2
e1b9ab6b09 fix: update copyright year 2016-02-20 02:06:14 +08:00
fengmk2
71f9014777 doc: fix disturl typo
Add aliyun to sponsors
2016-02-20 02:01:51 +08:00
fengmk2
adca5adff2 Merge pull request #822 from cnpm/sequelize
deps: sequelize@3.19.0
2016-02-19 23:35:25 +08:00
dead_horse
1857f1d0e5 deps: sequelize@3.19.0 2016-02-02 16:32:41 +08:00
dead_horse
2d56820e8b Release 2.7.1 2016-02-01 14:02:02 +08:00
Yiyu He
119094b230 Merge pull request #817 from cnpm/fix-semver
fix(semver): when have invalid version
2016-02-01 14:00:43 +08:00
dead_horse
744b6e1e15 fix(semver): when have invalid version 2016-02-01 13:55:12 +08:00
fengmk2
17f47732a0 Release 2.7.0 2016-02-01 13:16:35 +08:00
fengmk2
3a46b97544 Merge pull request #816 from cnpm/feat-semver
feat: support semver
2016-02-01 11:56:21 +08:00
dead_horse
44976f4afa test: fix all test cases 2016-02-01 01:42:09 +08:00
dead_horse
9da7c6ccf2 test: fix unpublish 2016-01-31 00:14:46 +08:00
dead_horse
4b68eecf53 test: add complex range test case 2016-01-30 23:08:27 +08:00
dead_horse
8d7058b947 feat: support semver
e.x. `/npm/~1.0.0`
2016-01-30 20:42:59 +08:00
dead_horse
933ae69d0b Release 2.6.2 2016-01-19 11:44:23 +08:00
Yiyu He
1af36f0a7a Merge pull request #799 from cnpm/jsonp
feat: list & show support jsonp
2016-01-19 11:43:24 +08:00
dead_horse
f4e9de5094 feat: list & show support jsonp 2016-01-18 11:07:21 +08:00
Yiyu He
2ccaf404ab Merge pull request #795 from cnpm/greenkeeper-urllib-2.7.0
Update urllib to version 2.7.0 🚀
2016-01-14 11:11:53 +08:00
greenkeeperio-bot
44c79c7646 chore(package): update urllib to version 2.7.0
http://greenkeeper.io/
2016-01-14 01:30:25 +08:00
alsotang
a845d3f74d Delete install.md
this file is deprecated, see https://github.com/cnpm/cnpmjs.org/wiki/Deploy for deploy
2016-01-13 11:54:59 +08:00
fengmk2
730113424c Release 2.6.1 2016-01-12 17:34:23 +08:00
fengmk2
658be3004f fix: source registry is not cnpm, ignore check status 2016-01-12 17:33:53 +08:00
fengmk2
214d0ad5c3 Release 2.6.0 2016-01-12 11:54:25 +08:00
Yiyu He
ca70c8a06d Merge pull request #786 from cnpm/add-sync-expired-check
feat(sync): monitor sync status
2016-01-12 10:20:02 +08:00
fengmk2
1c680367a7 feat(sync): monitor sync status
if last_sync_time > 2 x syncInterval, send the expired
warning message to admin
2016-01-12 10:11:20 +08:00
Yiyu He
3bd43d5ba9 Merge pull request #759 from cnpm/greenkeeper-urllib-2.6.0
Update urllib to version 2.6.0 🚀
2016-01-07 00:25:43 +08:00
Yiyu He
0965f8f8e5 Merge pull request #779 from cnpm/greenkeeper-agentkeepalive-2.0.3
Update agentkeepalive to version 2.0.3 🚀
2016-01-06 10:47:57 +08:00
greenkeeperio-bot
005eb603ae chore(package): update agentkeepalive to version 2.0.3
http://greenkeeper.io/
2016-01-05 20:40:22 +08:00
Yiyu He
65a9303dff Merge pull request #777 from gniavaj/master
fix SequelizeDatabaseError: ER_NO_SUCH_TABLE: Table 'qnpm.total' does…
2016-01-05 16:26:36 +08:00
gniavaj
3bc6b625ec fix SequelizeDatabaseError: ER_NO_SUCH_TABLE: Table 'qnpm.total' doesn't exist\nreproduce this bug:\nthe first startup of cnpmjs.org 2016-01-04 21:43:50 +08:00
Yiyu He
2b0dc6d17f Merge pull request #773 from cnpm/greenkeeper-moment-2.11.0
Update moment to version 2.11.0 🚀
2016-01-04 10:39:49 +08:00
Yiyu He
842d790dcb Merge pull request #774 from cnpm/greenkeeper-xss-0.2.10
xss@0.2.10 breaks build 🚨
2016-01-04 10:37:26 +08:00
greenkeeperio-bot
20eac48349 chore(package): update moment to version 2.11.0
http://greenkeeper.io/
2016-01-03 07:42:58 +08:00
greenkeeperio-bot
efb80e8c33 chore(package): update xss to version 0.2.10
http://greenkeeper.io/
2015-12-23 12:42:54 +08:00
alsotang
35fbc651cd remove bluebird
same as #767
2015-12-17 13:34:33 +08:00
Yiyu He
e55779ffa7 Merge pull request #767 from liyangready/master
remove bluebird
2015-12-17 11:12:23 +08:00
leon.lee
878176ea9c remove bluebird
maybe forget remove bluebird in this file
2015-12-17 11:08:49 +08:00
Yiyu He
e3a2eab000 Merge pull request #764 from cnpm/greenkeeper-pg-hstore-2.3.2
pg-hstore@2.3.2 breaks build ⚠️
2015-12-14 15:54:29 +08:00
greenkeeperio-bot
7478846754 chore(package): update pg-hstore to version 2.3.2
http://greenkeeper.io/
2015-12-14 08:31:56 +08:00
Yiyu He
8b244e6c26 Merge pull request #761 from cnpm/greenkeeper-mini-logger-1.1.0
Update mini-logger to version 1.1.0 🚀
2015-12-12 00:10:05 +08:00
greenkeeperio-bot
19217de010 chore(package): update mini-logger to version 1.1.0
http://greenkeeper.io/
2015-12-11 12:04:38 +08:00
greenkeeperio-bot
93a57b5dae chore(package): update urllib to version 2.6.0
http://greenkeeper.io/
2015-12-09 14:13:25 +08:00
Yiyu He
defb447f29 Merge pull request #756 from cnpm/fix-package-decode-error
fix: row.package will json parse error
2015-12-08 20:45:03 +08:00
fengmk2
93b73de164 Merge pull request #754 from cnpm/remove_bluebird
remove bluebird
2015-12-08 16:44:50 +08:00
fengmk2
ffc6124c0e fix: row.package will json parse error
need to ignore it.

also remove array.map on parseRow
2015-12-08 16:20:47 +08:00
Jackson Tian
ab40a992c2 remove bluebird 2015-12-08 12:03:36 +08:00
Yiyu He
5258569044 Merge pull request #751 from cnpm/greenkeeper-utility-1.6.0
Update utility to version 1.6.0 🚀
2015-12-05 00:26:52 +08:00
greenkeeperio-bot
04d8055d56 chore(package): update utility to version 1.6.0
http://greenkeeper.io/
2015-12-04 17:15:24 +08:00
fengmk2
68b6d54f60 Release 2.5.1 2015-12-02 23:54:28 +08:00
Yiyu He
a950a1e44b Merge pull request #749 from cnpm/greenkeeper-bluebird-3.0.6
bluebird@3.0.6 breaks build 🚨
2015-12-02 11:02:57 +08:00
greenkeeperio-bot
22f5472dbe chore(package): update bluebird to version 3.0.6
http://greenkeeper.io/
2015-12-02 01:06:03 +08:00
Yiyu He
d6d37a623d Merge pull request #744 from cnpm/fix_SequelizeDatabaseError
fix SequelizeDatabaseError
2015-11-27 17:02:59 +08:00
Jackson Tian
757184fbc8 fix SequelizeDatabaseError
SequelizeDatabaseError: ER_BAD_FIELD_ERROR: Unknown column 'NaN' in 'where clause'
2015-11-27 14:46:26 +08:00
Yiyu He
043fa2c32a Merge pull request #741 from cnpm/disable-delete-latest-tag
fix(dist_tag): disable delete latest tag
2015-11-24 22:31:06 +08:00
fengmk2
f94f5ca714 Merge pull request #736 from alsotang/analytics
use isoweek. a week start from monday
2015-11-24 18:48:41 +08:00
fengmk2
788bfc68bc fix(dist_tag): disable delete latest tag 2015-11-24 18:37:44 +08:00
fengmk2
dacc39c1d9 Merge pull request #738 from alsotang/patch-4
count total private pkgs
2015-11-23 23:07:08 +08:00
alsotang
3fb2228031 count total private pkgs 2015-11-23 22:51:38 +08:00
alsotang
4535178fa9 use isoweek. a week start from monday 2015-11-22 16:51:51 +08:00
Yiyu He
42e9e1603f Merge pull request #732 from cnpm/greenkeeper-semver-5.1.0
Update semver to version 5.1.0 🚀
2015-11-20 23:31:44 +08:00
Yiyu He
9a0c3c6c62 Merge pull request #733 from cnpm/greenkeeper-xss-0.2.8
Update xss to version 0.2.8 🚀
2015-11-20 23:31:19 +08:00
greenkeeperio-bot
ba864a62bf chore(package): update xss to version 0.2.8
http://greenkeeper.io/
2015-11-19 11:21:52 +08:00
greenkeeperio-bot
6dbfe18f58 chore(package): update semver to version 5.1.0
http://greenkeeper.io/
2015-11-19 07:18:18 +08:00
fengmk2
d8c65ff1f3 Release 2.5.0 2015-11-17 13:04:56 +08:00
Yiyu He
426990f94e Merge pull request #729 from cnpm/support-sync-deleted-user
feat(sync): sync deleted user
2015-11-17 11:09:30 +08:00
fengmk2
6d57929e89 test: add node v5 2015-11-17 10:24:52 +08:00
fengmk2
2b51171592 feat(sync): sync deleted user 2015-11-17 10:24:24 +08:00
Yiyu He
701c9ffa5f Merge pull request #728 from alsotang/patch-2
Update show.js
2015-11-16 17:26:07 +08:00
alsotang
4260b1c2b6 Update show.js
中文逗号

fix #727
2015-11-16 12:39:39 +08:00
Yiyu He
5e926f658d Merge pull request #725 from cnpm/greenkeeper-bytes-2.2.0
Update bytes to version 2.2.0 🚀
2015-11-14 16:16:22 +08:00
greenkeeperio-bot
78e13bb1d3 chore(package): update bytes to version 2.2.0
http://greenkeeper.io/
2015-11-14 09:44:10 +08:00
Yiyu He
9e5ec3406d Merge pull request #722 from alsotang/gzip_static
gzip static file
2015-11-13 00:24:01 +08:00
Yiyu He
21804d277b Merge pull request #717 from alsotang/no_sync_inner
do not sync inner username
2015-11-13 00:23:45 +08:00
alsotang
95d1a6b35c do not sync inner username 2015-11-12 22:07:33 +08:00
alsotang
78395d48b4 gzip static file 2015-11-12 22:04:45 +08:00
Yiyu He
c6795f201f Merge pull request #721 from cnpm/greenkeeper-bytes-2.1.0
Update bytes to version 2.1.0 🚀
2015-11-11 13:51:52 +08:00
greenkeeperio-bot
8e7a362bc2 chore(package): update bytes to version 2.1.0
http://greenkeeper.io/
2015-11-11 11:11:39 +08:00
Yiyu He
7ff3e9d017 Merge pull request #719 from cnpm/greenkeeper-is-type-of-1.0.0
Update is-type-of to version 1.0.0 🚀
2015-11-09 14:24:34 +08:00
greenkeeperio-bot
902b0bb638 chore(package): update is-type-of to version 1.0.0
http://greenkeeper.io/
2015-11-09 14:21:34 +08:00
Yiyu He
8097352a6c Merge pull request #714 from cnpm/greenkeeper-bluebird-3.0.5
Update bluebird to version 3.0.5 🚀
2015-11-08 23:21:26 +08:00
Yiyu He
ecf902b73e Merge pull request #718 from alsotang/patch-1
Update static.js
2015-11-08 23:14:14 +08:00
alsotang
bb0ace7e23 Update static.js 2015-11-08 18:51:45 +08:00
Yiyu He
d964b11b8c Merge pull request #715 from cnpm/greenkeeper-cfork-1.4.0
Update cfork to version 1.4.0 🚀
2015-11-04 12:07:16 +08:00
greenkeeperio-bot
53fa67f3bf chore(package): update cfork to version 1.4.0
http://greenkeeper.io/
2015-11-04 11:46:28 +08:00
greenkeeperio-bot
adf7ece3e9 chore(package): update bluebird to version 3.0.5
http://greenkeeper.io/
2015-11-01 22:40:59 +08:00
fengmk2
02d5b4d16b Release 2.4.1 2015-10-27 20:50:40 +08:00
Yiyu He
67f8e64140 Merge pull request #703 from cnpm/registry-index-cache
fix: improve registry index page performance with cache
2015-10-27 15:32:52 +08:00
fengmk2
45ceb17238 fix: improve registry index page performance with cache
cache total info in 10s

closes #695
2015-10-27 14:33:30 +08:00
fengmk2
a1ee99618c Merge pull request #701 from cnpm/greenkeeper-koa-mock-1.5.0
Update koa-mock to version 1.5.0 🚀
2015-10-27 14:23:41 +08:00
fengmk2
de854a76f1 Merge pull request #702 from ibigbug/configable-badge-url
Configable badge url
2015-10-27 13:59:04 +08:00
ibigbug
d59ebb44be test case. 2015-10-27 12:27:25 +08:00
yuwei.byw
5a2ac54c73 Use shield offical url prefix. 2015-10-27 11:54:11 +08:00
Yuwei Ba
8fd8bae5aa Configable badge URL prefix. 2015-10-27 11:52:38 +08:00
greenkeeperio-bot
5929b9b1ea chore(package): update koa-mock to version 1.5.0
http://greenkeeper.io/
2015-10-27 11:33:27 +08:00
Yiyu He
aae14f1a6e Merge pull request #697 from cnpm/greenkeeper-semver-5.0.3
Update semver to version 5.0.3 🚀
2015-10-27 00:47:20 +08:00
Yiyu He
96ad637882 Merge pull request #698 from cnpm/greenkeeper-co-4.6.0
Update co to version 4.6.0 🚀
2015-10-27 00:46:49 +08:00
Yiyu He
73aa9129a8 Merge pull request #700 from cnpm/greenkeeper-urllib-2.5.0
Update urllib to version 2.5.0 🚀
2015-10-27 00:46:37 +08:00
greenkeeperio-bot
1c89a6f6f9 chore(package): update urllib to version 2.5.0
http://greenkeeper.io/
2015-10-26 20:15:04 +08:00
greenkeeperio-bot
c2beb96dda chore(package): update co to version 4.6.0
http://greenkeeper.io/
2015-10-24 06:09:06 +08:00
greenkeeperio-bot
4c4fc3fc7f chore(package): update semver to version 5.0.3
http://greenkeeper.io/
2015-10-23 17:10:01 +08:00
fengmk2
82395fdc33 Release 2.4.0 2015-10-21 22:36:45 +08:00
fengmk2
ced4887269 Merge pull request #696 from cnpm/dependents
feat(registry): add package's dependents api
2015-10-21 21:32:39 +08:00
fengmk2
8f96a5a57b feat(registry): add package's dependents api
```
GET /-/package/:name/dependents

{
  "dependents": [ "pkg1", "pkg2" ]
}
```

closes #694
2015-10-21 21:24:59 +08:00
fengmk2
f6e79ff5e7 fix: show package's dependents
closes #694
2015-10-21 21:06:27 +08:00
fengmk2
e932f16424 Merge pull request #690 from cnpm/greenkeeper-bluebird-2.10.2
Update bluebird to version 2.10.2 🚀
2015-10-21 18:44:55 +08:00
fengmk2
622c93c3fe Merge pull request #693 from cnpm/greenkeeper-utility-1.5.0
Update utility to version 1.5.0 🚀
2015-10-21 18:44:28 +08:00
greenkeeperio-bot
ccf3e8a83c chore(package): update utility to version 1.5.0
http://greenkeeper.io/
2015-10-20 21:45:36 +08:00
greenkeeperio-bot
2069b4334c chore(package): update bluebird to version 2.10.2
http://greenkeeper.io/
2015-10-16 21:40:55 +08:00
fengmk2
ef84cedd79 Release 2.3.1 2015-10-15 23:41:13 +08:00
fengmk2
ba07f87ee0 Merge pull request #687 from cnpm/upgrade-giturl
deps: upgrade giturl
2015-10-15 22:00:23 +08:00
fengmk2
10cbe70378 refactor: remove gnode 2015-10-15 21:26:06 +08:00
fengmk2
244ce60857 deps: upgrade giturl
closes #685
2015-10-15 21:22:39 +08:00
fengmk2
97a4f6cd92 Release 2.3.0 2015-10-15 21:22:08 +08:00
fengmk2
10197ef2aa Merge pull request #686 from ibigbug/ant-design-fix
Some fixes
2015-10-15 19:33:13 +08:00
Yuwei Ba
d2e7350b27 Some fixes
* Header overflow
* Empty alert
* Headline font-family
2015-10-15 18:59:50 +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
fengmk2
e0eeefa43e Release 2.1.0 2015-07-08 23:21:19 +08:00
Yiyu He
662b91ea9a Merge pull request #630 from cnpm/search-support-jsonp
feat(web): search support jsonp
2015-07-08 22:19:55 +08:00
fengmk2
cc70f9e6b1 feat(web): search support jsonp
http://localhost:7002/browse/keyword/mk2?type=json&callback=foo
2015-07-08 21:06:14 +08:00
dead_horse
7312d761ca fix function name 2015-06-15 23:10:39 +08:00
fengmk2
d5c0e07c1c Release 2.0.0 2015-05-11 22:48:35 +08:00
fengmk2
e108b4804d Merge pull request #608 from cnpm/http-proxy
feat(urllib): support http_proxy
2015-05-11 15:25:33 +08:00
Yiyu He
92c218d755 Merge pull request #610 from cnpm/download-as-stream
fix: real download as stream
2015-04-20 00:47:02 +08:00
fengmk2
df2a974b2c fix: real download as stream 2015-04-20 00:40:06 +08:00
fengmk2
7fc1a8411c add custom ad banner config 2015-03-28 09:53:44 +08:00
fengmk2
0759328140 add sponsors: ucloud.cn 2015-03-23 09:31:02 +08:00
Yiyu He
3fc2ea7f25 Merge pull request #609 from limianwang/master
fix small typo
2015-03-09 14:14:19 +08:00
Limian Wang
2f2794785f fix small typo 2015-03-08 23:03:52 -07:00
fengmk2
ea1f253624 feat(urllib): support http_proxy
Fixes #607
2015-03-09 11:25:05 +08:00
fengmk2
b25eaaf73e force using https links 2015-02-16 11:19:18 +08:00
fengmk2
0b0164cbf4 Release 2.0.0-rc.15 2015-02-15 11:07:21 +08:00
Yiyu He
8a727c351d Merge pull request #602 from cnpm/pg
feat(database): support PostgreSQL
2015-02-14 23:52:26 +08:00
fengmk2
2c6a08908d fix(markdown): filter xss after markdown render 2015-02-14 22:02:31 +08:00
fengmk2
9a7e94f7a4 feat(database): support PostgreSQL
And the big problem of pg is bigint will be convert to string

Fixes #309
Fixes #489
2015-02-14 17:54:04 +08:00
fengmk2
9a2fba42c6 Release 2.0.0-rc.14 2015-02-14 10:21:21 +08:00
fengmk2
ab8d6bfd19 fix config load flows 2015-02-14 10:15:43 +08:00
Yiyu He
619f4310e8 Merge pull request #601 from cnpm/always-auth
feat: support always-auth
2015-02-14 10:10:20 +08:00
fengmk2
41eea6747d feat: support always-auth
Add `alwaysAuth: true` to config and set npm cli config.
Use 'http://registry.sample.com' for example, set npm config below:

    $ npm config set '//registry.sample.com/:always-auth=true'

Fixes #600
2015-02-13 20:39:40 +08:00
fengmk2
4e3a50f3bb fix mysql select args = [] bug 2015-02-13 17:51:50 +08:00
fengmk2
e5a9a22f86 fix #597 sequelize raw query. 2015-02-13 17:40:40 +08:00
fengmk2
eb66d8958f fix(markdown): hotfix markdown-it cpu problem
Fixes #598
2015-02-13 17:14:23 +08:00
Yiyu He
8a0dcb5063 Merge pull request #596 from cnpm/co4
feat: upgrade to co4
2015-02-10 09:43:16 +08:00
fengmk2
cba9d5dbbb feat: upgrade to co4 2015-02-10 09:00:55 +08:00
Yiyu He
233b736401 Merge pull request #595 from cnpm/kcors
use kcors fixes #594
2015-02-09 14:05:28 +08:00
fengmk2
58d72bde22 use kcors fixes #594 2015-02-09 14:01:03 +08:00
128 changed files with 3512 additions and 1650 deletions

View File

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

View File

@@ -1,6 +1,10 @@
sudo: false
language: node_js language: node_js
node_js: node_js:
- '0.11.14' - '4'
before_install: "npm install --build-from-source" - '6'
script: "make test-travis-all" addons:
after_script: "npm install coveralls@2 && cat ./coverage/lcov.info | coveralls" - postgresql: '9.3'
script: 'make test-travis-all'
after_script:
- 'npm i codecov && codecov'

View File

@@ -5,3 +5,7 @@ dead-horse <dead_horse@qq.com> (https://github.com/dead-horse)
alsotang <alsotang@gmail.com> (https://github.com/alsotang) alsotang <alsotang@gmail.com> (https://github.com/alsotang)
4simple <wondger@qq.com> (https://github.com/4simple) 4simple <wondger@qq.com> (https://github.com/4simple)
afc163 <afc163@gmail.com> (https://github.com/afc163) afc163 <afc163@gmail.com> (https://github.com/afc163)
Yuwei Ba <i@xiaoba.me> (https://github.com/ibigbug)
dickeylth <dickeylth@gmail.com> (https://github.com/dickeylth)

View File

@@ -1,4 +1,312 @@
2.13.0 / 2016-07-26
==================
* feat: enable maxrequests middleware (#1003)
2.12.2 / 2016-07-11
==================
* fix: getModuleByRange don't list all packages (#990)
* fix: should show new version package count (#984)
2.12.1 / 2016-07-01
==================
* fix: make sure chagnes stream destroy (#982)
* chore(package): update semver to version 5.2.0 (#978)
* deps: use ^ instead of ~ (#976)
* chore(package): update mini-logger to version 1.1.1 (#973)
2.12.0 / 2016-06-26
==================
* fix: logger seperator should be one EOL ([#972](https://github.com/cnpm/cnpmjs.org/issues/972))
* feat: add security check badge for public package ([#971](https://github.com/cnpm/cnpmjs.org/issues/971))
2.11.0 / 2016-06-25
==================
* feat: add changes stream syncer ([#970](https://github.com/cnpm/cnpmjs.org/issues/970))
* chore(package): update pg to version 5.1.0 ([#953](https://github.com/cnpm/cnpmjs.org/issues/953))
2.10.1 / 2016-06-05
==================
* fix: should sync missing public scoped package on install ([#946](https://github.com/cnpm/cnpmjs.org/issues/946))
* chore(package): update bytes to version 2.4.0 ([#943](https://github.com/cnpm/cnpmjs.org/issues/943))
* userService ([#926](https://github.com/cnpm/cnpmjs.org/issues/926))
* chore(package): update should to version 8.4.0 ([#928](https://github.com/cnpm/cnpmjs.org/issues/928))
* chore(package): update humanize-ms to version 1.2.0 ([#927](https://github.com/cnpm/cnpmjs.org/issues/927))
* chore(package): update kcors to version 1.2.1 ([#918](https://github.com/cnpm/cnpmjs.org/issues/918))
* chore(package): update urllib to version 2.9.0 ([#898](https://github.com/cnpm/cnpmjs.org/issues/898))
2.10.0 / 2016-04-15
==================
* feat: show tarball url on package page ([#894](https://github.com/cnpm/cnpmjs.org/issues/894))
* chore(package): update koa-mock to version 1.6.1 ([#891](https://github.com/cnpm/cnpmjs.org/issues/891))
2.9.5 / 2016-04-12
==================
* fix: change logo url to a better https source
* fix: http://cnpmjs.org/package/fms pre style ([#739](https://github.com/cnpm/cnpmjs.org/issues/739))
2.9.4 / 2016-04-09
==================
* fix: don't sync constructor package on exists mode ([#883](https://github.com/cnpm/cnpmjs.org/issues/883))
* Update utility to version 1.7.0 🚀
* chore: update sponsor link
2.9.3 / 2016-04-05
==================
* fix: use better diff time to check sync status
* Update sequelize to version 3.21.0 🚀
* chore(package): update agentkeepalive to version 2.1.0
* chore(package): update pg to version 4.5.2
2.9.2 / 2016-03-29
==================
* fix: override antd for ul & ol list number & icon.
2.9.1 / 2016-03-29
==================
* refactor: add more ua info on syncer
2.9.0 / 2016-03-26
==================
* feat: only admin can unpublish
* chore(package): update gravatar to version 1.5.0
* chore(package): update sequelize to version 3.20.0
* fix: fix save download count unqiue constraint error
* chore(package): update moment to version 2.12.0
2.8.1 / 2016-03-07
==================
* fix: only send warning email if no any sync data after 24h
* chore(package): update kcors to version 1.1.0
* chore(package): update koa to version 1.2.0
* chore(package): update urllib to version 2.8.0
2.8.0 / 2016-02-23
==================
* fix: convert `*` to latest tag
* deps: upgrade deps and remove node 2.0.0 support
* doc: update sponsors on readme
* fix: update copyright year
* doc: fix disturl typo
* deps: sequelize@3.19.0
2.7.1 / 2016-02-01
==================
* fix(semver): when have invalid version([#817](https://github.com/cnpm/cnpmjs.org/issues/817))
2.7.0 / 2016-02-01
==================
* test: fix all test cases
* test: fix unpublish
* test: add complex range test case
* feat: support semver([#816](https://github.com/cnpm/cnpmjs.org/issues/816))
2.6.2 / 2016-01-19
==================
* feat: list & show support jsonp
* chore(package): update urllib to version 2.7.0
* Delete install.md
2.6.1 / 2016-01-12
==================
* fix: source registry is not cnpm, ignore check status
2.6.0 / 2016-01-12
==================
* feat(sync): monitor sync status
* chore(package): update agentkeepalive to version 2.0.3
* fix SequelizeDatabaseError: ER_NO_SUCH_TABLE: Table 'qnpm.total' doesn't exist\nreproduce this bug:\nthe first startup of cnpmjs.org
* chore(package): update moment to version 2.11.0
* chore(package): update xss to version 0.2.10
* chore(package): update pg-hstore to version 2.3.2
* chore(package): update mini-logger to version 1.1.0
* chore(package): update urllib to version 2.6.0
* fix: row.package will json parse error
* remove bluebird
* chore(package): update utility to version 1.6.0
2.5.1 / 2015-12-02
==================
* chore(package): update bluebird to version 3.0.6
* fix: SequelizeDatabaseError
* fix(dist_tag): disable delete latest tag
* feat: count total private pkgs
* fix: use isoweek. a week start from monday
* chore(package): update xss to version 0.2.8
* chore(package): update semver to version 5.1.0
2.5.0 / 2015-11-17
==================
* test: add node v5
* feat(sync): sync deleted user
* Update show.js
* chore(package): update bytes to version 2.2.0
* do not sync inner username
* gzip static file
* chore(package): update bytes to version 2.1.0
* chore(package): update is-type-of to version 1.0.0
* Update static.js
* chore(package): update cfork to version 1.4.0
* chore(package): update bluebird to version 3.0.5
2.4.1 / 2015-10-27
==================
* fix: improve registry index page performance with cache
* Configable badge URL prefix.
* chore(package): update koa-mock to version 1.5.0
* chore(package): update urllib to version 2.5.0
* chore(package): update co to version 4.6.0
* chore(package): update semver to version 5.0.3
2.4.0 / 2015-10-21
==================
* feat(registry): add package's dependents api
* fix: show package's dependents
* chore(package): update utility to version 1.5.0
* chore(package): update bluebird to version 2.10.2
2.3.1 / 2015-10-15
==================
* refactor: remove gnode
* deps: upgrade giturl
* Some fixes
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](https://github.com/cnpm/cnpmjs.org/issues/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
==================
* feat(web): search support jsonp
* fix function name
2.0.0 / 2015-05-11
==================
* fix: real download as stream
* add custom ad banner config
* add sponsors: ucloud.cn
* fix small typo
* feat(urllib): support http_proxy
* force using https links
2.0.0-rc.15 / 2015-02-15
==================
* fix(markdown): filter xss after markdown render
* feat(database): support PostgreSQL
2.0.0-rc.14 / 2015-02-14
==================
* feat: support always-auth
* fix mysql select args = [] bug
* fix [#597](https://github.com/cnpm/cnpmjs.org/issues/597) sequelize raw query.
* fix(markdown): hotfix markdown-it cpu problem
* feat: upgrade to co4
* use kcors fixes [#594](https://github.com/cnpm/cnpmjs.org/issues/594)
2.0.0-rc.13 / 2015-02-04 2.0.0-rc.13 / 2015-02-04
================== ==================

View File

@@ -1,6 +1,6 @@
This software is licensed under the MIT License. This software is licensed under the MIT License.
Copyright(c) cnpmjs.org and other contributors. Copyright(c) cnpm and other contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -2,34 +2,29 @@ TESTS = $(shell ls -S `find test -type f -name "*.test.js" -print`)
REPORTER = spec REPORTER = spec
TIMEOUT = 30000 TIMEOUT = 30000
MOCHA_OPTS = MOCHA_OPTS =
REGISTRY = --registry=https://registry.npm.taobao.org
DB = sqlite DB = sqlite
install: jshint:
@npm install --build-from-source $(REGISTRY) \ @node_modules/.bin/jshint .
--disturl=https://npm.taobao.org/dist
install-production production:
@NODE_ENV=production $(MAKE) install
jshint: install
@-node_modules/.bin/jshint ./
init-database: init-database:
@node --harmony test/init_db.js @node test/init_db.js
init-mysql: init-mysql:
@mysql -uroot -e 'DROP DATABASE IF EXISTS cnpmjs_test;' @mysql -uroot -e 'DROP DATABASE IF EXISTS cnpmjs_test;'
@mysql -uroot -e 'CREATE DATABASE cnpmjs_test;' @mysql -uroot -e 'CREATE DATABASE cnpmjs_test;'
test: install init-database init-pg:
@psql -c 'DROP DATABASE IF EXISTS cnpmjs_test;'
@psql -c 'CREATE DATABASE cnpmjs_test;'
test: init-database
@NODE_ENV=test DB=${DB} node_modules/.bin/mocha \ @NODE_ENV=test DB=${DB} node_modules/.bin/mocha \
--harmony \
--reporter $(REPORTER) \ --reporter $(REPORTER) \
--timeout $(TIMEOUT) \ --timeout $(TIMEOUT) \
--require should \ --require should \
--require should-http \ --require should-http \
--require co-mocha \ --require thunk-mocha \
--require ./test/init.js \ --require ./test/init.js \
$(MOCHA_OPTS) \ $(MOCHA_OPTS) \
$(TESTS) $(TESTS)
@@ -40,18 +35,21 @@ test-sqlite:
test-mysql: init-mysql test-mysql: init-mysql
@$(MAKE) test DB=mysql @$(MAKE) test DB=mysql
test-pg: init-pg
@DB_PORT=5432 DB_USER=$(USER) $(MAKE) test DB=postgres
test-all: test-sqlite test-mysql test-all: test-sqlite test-mysql
test-cov cov: install init-database test-cov cov: init-database
@NODE_ENV=test DB=${DB} node --harmony \ @NODE_ENV=test DB=${DB} node \
node_modules/.bin/istanbul cover --preserve-comments \ node_modules/.bin/istanbul cover \
node_modules/.bin/_mocha \ node_modules/.bin/_mocha \
-- -u exports \ -- -u exports \
--reporter $(REPORTER) \ --reporter $(REPORTER) \
--timeout $(TIMEOUT) \ --timeout $(TIMEOUT) \
--require should \ --require should \
--require should-http \ --require should-http \
--require co-mocha \ --require thunk-mocha \
--require ./test/init.js \ --require ./test/init.js \
$(MOCHA_OPTS) \ $(MOCHA_OPTS) \
$(TESTS) $(TESTS)
@@ -62,18 +60,17 @@ test-cov-sqlite:
test-cov-mysql: init-mysql test-cov-mysql: init-mysql
@$(MAKE) test-cov DB=mysql @$(MAKE) test-cov DB=mysql
test-travis: install init-database test-travis: init-database
@NODE_ENV=test DB=${DB} CNPM_SOURCE_NPM=https://registry.npmjs.org CNPM_SOURCE_NPM_ISCNPM=false \ @NODE_ENV=test DB=${DB} CNPM_SOURCE_NPM=https://registry.npmjs.com CNPM_SOURCE_NPM_ISCNPM=false \
node --harmony \ node \
node_modules/.bin/istanbul cover --preserve-comments \ node_modules/.bin/istanbul cover \
node_modules/.bin/_mocha \ node_modules/.bin/_mocha \
--report lcovonly \ -- -u exports \
-- \
--reporter dot \ --reporter dot \
--timeout $(TIMEOUT) \ --timeout $(TIMEOUT) \
--require should \ --require should \
--require should-http \ --require should-http \
--require co-mocha \ --require thunk-mocha \
--require ./test/init.js \ --require ./test/init.js \
$(MOCHA_OPTS) \ $(MOCHA_OPTS) \
$(TESTS) $(TESTS)
@@ -84,20 +81,24 @@ test-travis-sqlite:
test-travis-mysql: init-mysql test-travis-mysql: init-mysql
@$(MAKE) test-travis DB=mysql @$(MAKE) test-travis DB=mysql
test-travis-all: test-travis-sqlite test-travis-mysql test-travis-pg:
@psql -c 'DROP DATABASE IF EXISTS cnpmjs_test;' -U postgres
@psql -c 'CREATE DATABASE cnpmjs_test;' -U postgres
@DB_PORT=5432 DB_USER=postgres $(MAKE) test-travis DB=postgres
test-travis-all: jshint test-travis-sqlite test-travis-mysql test-travis-pg
dev: dev:
@NODE_ENV=development node_modules/.bin/node-dev --harmony dispatch.js @NODE_ENV=development node node_modules/.bin/node-dev dispatch.js
contributors: install contributors:
@node_modules/.bin/contributors -f plain -o AUTHORS @node_modules/.bin/contributors -f plain -o AUTHORS
autod: install autod:
@node_modules/.bin/autod -w \ @node_modules/.bin/autod -w \
--prefix "~" \ --prefix "~" \
--exclude public,view,docs,backup,coverage \ --exclude public,view,docs,backup,coverage \
--dep bluebird,mysql \ --dep mysql \
--keep should,supertest,should-http,chunkstream,mm,pedding --keep should,supertest,should-http,chunkstream,mm,pedding
@$(MAKE) install
.PHONY: test .PHONY: test

View File

@@ -3,29 +3,23 @@ cnpmjs.org
[![NPM version][npm-image]][npm-url] [![NPM version][npm-image]][npm-url]
[![build status][travis-image]][travis-url] [![build status][travis-image]][travis-url]
[![Test coverage][coveralls-image]][coveralls-url] [![Test coverage][codecov-image]][codecov-url]
[![Gittip][gittip-image]][gittip-url]
[![David deps][david-image]][david-url] [![David deps][david-image]][david-url]
[![node version][node-image]][node-url] [![Known Vulnerabilities][snyk-image]][snyk-url]
[![npm download][download-image]][download-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-image]: http://cnpmjs.org/badge/v/cnpmjs.org.svg?style=flat-square
[npm-url]: http://cnpmjs.org/package/cnpmjs.org [npm-url]: http://cnpmjs.org/package/cnpmjs.org
[travis-image]: https://img.shields.io/travis/cnpm/cnpmjs.org.svg?style=flat-square [travis-image]: https://img.shields.io/travis/cnpm/cnpmjs.org.svg?style=flat-square
[travis-url]: https://travis-ci.org/cnpm/cnpmjs.org [travis-url]: https://travis-ci.org/cnpm/cnpmjs.org
[coveralls-image]: https://img.shields.io/coveralls/cnpm/cnpmjs.org.svg?style=flat-square [codecov-image]: https://codecov.io/gh/cnpm/cnpmjs.org/branch/master/graph/badge.svg
[coveralls-url]: https://coveralls.io/r/cnpm/cnpmjs.org?branch=master [codecov-url]: https://codecov.io/gh/cnpm/cnpmjs.org
[gittip-image]: https://img.shields.io/gittip/fengmk2.svg?style=flat-square
[gittip-url]: https://www.gittip.com/fengmk2/
[david-image]: https://img.shields.io/david/cnpm/cnpmjs.org.svg?style=flat-square [david-image]: https://img.shields.io/david/cnpm/cnpmjs.org.svg?style=flat-square
[david-url]: https://david-dm.org/cnpm/cnpmjs.org [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 [snyk-image]: https://snyk.io/test/npm/cnpmjs.org/badge.svg?style=flat-square
[node-url]: http://nodejs.org/download/ [snyk-url]: https://snyk.io/test/npm/cnpmjs.org
[download-image]: https://img.shields.io/npm/dm/cnpmjs.org.svg?style=flat-square [download-image]: https://img.shields.io/npm/dm/cnpmjs.org.svg?style=flat-square
[download-url]: https://npmjs.org/package/cnpmjs.org [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) ![logo](https://raw.github.com/cnpm/cnpmjs.org/master/logo.png)
@@ -60,6 +54,7 @@ And it easy to wrap for your own registry which build with `cnpmjs.org`.
* **Compatible with NPM client**: you can use the origin NPM client with `cnpmjs.org`, * **Compatible with NPM client**: you can use the origin NPM client with `cnpmjs.org`,
only need to change the registry in config. Even include manual synchronization (through `install` command). only need to change the registry in config. Even include manual synchronization (through `install` command).
* **Version badge**: base on [shields.io](http://shields.io/) ![cnpm-badge](http://cnpmjs.org/badge/v/cnpmjs.org.svg?style=flat-square) * **Version badge**: base on [shields.io](http://shields.io/) ![cnpm-badge](http://cnpmjs.org/badge/v/cnpmjs.org.svg?style=flat-square)
* **Support http_proxy**: if you're behind firewall, need to request through http proxy
**PROTIP** Be sure to read [Migrating from 1.x to 2.x](https://github.com/cnpm/cnpmjs.org/wiki/Migrating-from-1.x-to-2.x) **PROTIP** Be sure to read [Migrating from 1.x to 2.x](https://github.com/cnpm/cnpmjs.org/wiki/Migrating-from-1.x-to-2.x)
as well as [New features in 2.x](https://github.com/cnpm/cnpmjs.org/wiki/New-features-in-2.x). as well as [New features in 2.x](https://github.com/cnpm/cnpmjs.org/wiki/New-features-in-2.x).
@@ -72,13 +67,14 @@ as well as [New features in 2.x](https://github.com/cnpm/cnpmjs.org/wiki/New-fea
* Mirror NPM in China: [cnpmjs.org](http://cnpmjs.org) * Mirror NPM in China: [cnpmjs.org](http://cnpmjs.org)
* cnpm client: [cnpm](https://github.com/cnpm/cnpm), `npm install -g cnpm` * cnpm client: [cnpm](https://github.com/cnpm/cnpm), `npm install -g cnpm`
* [How to deploy cnpmjs.org](https://github.com/cnpm/cnpmjs.org/wiki/Deploy) * [How to deploy cnpmjs.org](https://github.com/cnpm/cnpmjs.org/wiki/Deploy)
* [Sync packages through `http_proxy`](https://github.com/cnpm/cnpmjs.org/wiki/Sync-packages-through-http_proxy)
* [wiki](https://github.com/cnpm/cnpmjs.org/wiki) * [wiki](https://github.com/cnpm/cnpmjs.org/wiki)
## Develop on your local machine ## Develop on your local machine
### Dependencies ### Dependencies
* [node](http://nodejs.org) >=0.11.12, use `--harmony` * [node](http://nodejs.org) >= 4.3.1
* Databases: only required one type * Databases: only required one type
* [sqlite3](https://npm.taobao.org/package/sqlite3) >= 3.0.2, we use `sqlite3` by default * [sqlite3](https://npm.taobao.org/package/sqlite3) >= 3.0.2, we use `sqlite3` by default
* [MySQL](http://dev.mysql.com/downloads/) >= 0.5.0, include `mysqld` and `mysql cli`. I test on `mysql@5.6.16`. * [MySQL](http://dev.mysql.com/downloads/) >= 0.5.0, include `mysqld` and `mysql cli`. I test on `mysql@5.6.16`.
@@ -100,7 +96,7 @@ $ make test
# coverage # coverage
$ make test-cov $ make test-cov
# udpate dependencies # update dependencies
$ make autod $ make autod
# start server with development mode # start server with development mode
@@ -116,6 +112,11 @@ $ make dev
Tips: make sure your code is following the [node-style-guide](https://github.com/felixge/node-style-guide). Tips: make sure your code is following the [node-style-guide](https://github.com/felixge/node-style-guide).
## Sponsors
- [![阿里云](https://static.aliyun.com/images/www-summerwind/logo.gif)](http://click.aliyun.com/m/4288/) (2016.2 - now)
- [![UCloud云计算](https://www.ucloud.cn/static/style/images/about/logo.png)](http://www.ucloud.cn?sem=sdk-CNPMJS) (2015.3 - 2016.3)
## License ## License
MIT [MIT](LICENSE.txt)

View File

@@ -1,9 +1,7 @@
#!/usr/bin/env node #!/usr/bin/env node
/**! /**!
* cnpmjs.org - bin/cli.js * Copyright(c) cnpm and other contributors.
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
@@ -16,7 +14,6 @@
* Module dependencies. * Module dependencies.
*/ */
require('gnode');
var debug = require('debug')('cnpmjs.org:cli'); var debug = require('debug')('cnpmjs.org:cli');
var program = require('commander'); var program = require('commander');
var path = require('path'); var path = require('path');
@@ -86,9 +83,10 @@ function start(options) {
debug('save config %s to %s', configJSON, configfile); debug('save config %s to %s', configJSON, configfile);
// if sqlite db file not exists, init first // if sqlite db file not exists, init first
initDatabase(); initDatabase(function() {
require('../dispatch');
});
require('../dispatch');
fs.writeFileSync(path.join(dataDir, 'pid'), process.pid + ''); fs.writeFileSync(path.join(dataDir, 'pid'), process.pid + '');
} }
@@ -108,7 +106,7 @@ function stop(options) {
} }
} }
function initDatabase() { function initDatabase(callback) {
var models = require('../models'); var models = require('../models');
models.sequelize.sync({ force: false }) models.sequelize.sync({ force: false })
@@ -120,6 +118,7 @@ function initDatabase() {
throw err; throw err;
} else { } else {
console.log('[models/init_script.js] `sqlite` sequelize sync and init success'); console.log('[models/init_script.js] `sqlite` sequelize sync and init success');
callback();
} }
}); });
}) })

View File

@@ -7,7 +7,7 @@ export NODE_ENV='production'
ulimit -c unlimited ulimit -c unlimited
cd `dirname $0`/.. cd `dirname $0`/..
NODEJS='node --harmony' NODEJS='node'
BASE_HOME=`pwd` BASE_HOME=`pwd`
PROJECT_NAME=`basename ${BASE_HOME}` PROJECT_NAME=`basename ${BASE_HOME}`
STDOUT_LOG=`$NODEJS -e "console.log(require('path').join(require('$BASE_HOME/config').logdir, 'nodejs_stdout.log'));\ STDOUT_LOG=`$NODEJS -e "console.log(require('path').join(require('$BASE_HOME/config').logdir, 'nodejs_stdout.log'));\

View File

@@ -1,62 +1,56 @@
/**!
* cnpmjs.org - common/logger.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict'; 'use strict';
/** const debug = require('debug')('cnpmjs.org:logger');
* Module dependencies. const formater = require('error-formater');
*/ const Logger = require('mini-logger');
const utility = require('utility');
const util = require('util');
const os = require('os');
const config = require('../config');
const mail = require('./mail');
var formater = require('error-formater'); const isTEST = process.env.NODE_ENV === 'test';
var Logger = require('mini-logger'); const categories = ['sync_info', 'sync_error'];
var utility = require('utility');
var util = require('util');
var config = require('../config');
var mail = require('./mail');
var isTEST = process.env.NODE_ENV === 'test'; const logger = module.exports = Logger({
var categories = ['sync_info', 'sync_error'];
var logger = module.exports = Logger({
categories: categories, categories: categories,
dir: config.logdir, dir: config.logdir,
duration: '1d', duration: '1d',
format: '[{category}.]YYYY-MM-DD[.log]', format: '[{category}.]YYYY-MM-DD[.log]',
stdout: config.debug && !isTEST, stdout: config.debug && !isTEST,
errorFormater: errorFormater errorFormater: errorFormater,
seperator: os.EOL,
}); });
var to = []; const to = [];
for (var user in config.admins) { for (var user in config.admins) {
to.push(config.admins[user]); to.push(config.admins[user]);
} }
function errorFormater(err) { function errorFormater(err) {
var msg = formater.both(err); const msg = formater.both(err);
mail.error(to, msg.json.name, msg.text); mail.error(to, msg.json.name, msg.text);
return msg.text; return msg.text;
} }
logger.syncInfo = function () { logger.syncInfo = function () {
var args = [].slice.call(arguments); const args = [].slice.call(arguments);
if (typeof args[0] === 'string') { if (typeof args[0] === 'string') {
args[0] = util.format('[%s][%s] ', utility.logDate(), process.pid) + args[0]; args[0] = util.format('[%s][%s] ', utility.logDate(), process.pid) + args[0];
} }
if (debug.enabled) {
debug.apply(debug, args);
}
logger.sync_info.apply(logger, args); logger.sync_info.apply(logger, args);
}; };
logger.syncError =function () { logger.syncError =function () {
var args = [].slice.call(arguments); const args = [].slice.call(arguments);
if (typeof args[0] === 'string') { if (typeof args[0] === 'string') {
args[0] = util.format('[%s][%s] ', utility.logDate(), process.pid) + args[0]; args[0] = util.format('[%s][%s] ', utility.logDate(), process.pid) + args[0];
} }
if (debug.enabled) {
debug.apply(debug, args);
}
logger.sync_error.apply(logger, arguments); logger.sync_error.apply(logger, arguments);
}; };

View File

@@ -14,10 +14,10 @@
* Module dependencies. * Module dependencies.
*/ */
var mailConfig = require('../config').mail;
var nodemailer = require('nodemailer'); var nodemailer = require('nodemailer');
var utility = require('utility'); var utility = require('utility');
var os = require('os'); var os = require('os');
var mailConfig = require('../config').mail;
var smtpConfig; var smtpConfig;
if (mailConfig.auth) { if (mailConfig.auth) {
@@ -25,6 +25,7 @@ if (mailConfig.auth) {
smtpConfig = mailConfig; smtpConfig = mailConfig;
} else { } else {
smtpConfig = { smtpConfig = {
enable: mailConfig.enable,
// backward compat // backward compat
host: mailConfig.host, host: mailConfig.host,
port: mailConfig.port, port: mailConfig.port,
@@ -37,7 +38,7 @@ if (mailConfig.auth) {
}; };
} }
var transport = nodemailer.createTransport(smtpConfig); var transport;
/** /**
* Send notice email with mail level and appname. * Send notice email with mail level and appname.
@@ -71,6 +72,15 @@ LEVELS.forEach(function (level) {
exports.send = function (to, subject, html, callback) { exports.send = function (to, subject, html, callback) {
callback = callback || utility.noop; callback = callback || utility.noop;
if (mailConfig.enable === false) {
console.log('[send mail debug] [%s] to: %s, subject: %s\n%s', Date(), to, subject, html);
return callback();
}
if (!transport) {
transport = nodemailer.createTransport(smtpConfig);
}
var message = { var message = {
from: mailConfig.from || mailConfig.sender, from: mailConfig.from || mailConfig.sender,
to: to, to: to,

View File

@@ -17,11 +17,18 @@
var xss = require('xss'); var xss = require('xss');
var MarkdownIt = require('markdown-it'); var MarkdownIt = require('markdown-it');
// allow class attr on code
xss.whiteList.code = ['class'];
var md = new MarkdownIt({ var md = new MarkdownIt({
html: true, html: true,
linkify: true, linkify: true,
}); });
exports.render = function (content, filterXss) { exports.render = function (content, filterXss) {
return md.render(filterXss === false ? content : xss(content)); var html = md.render(content);
if (filterXss !== false) {
html = xss(html);
}
return html;
}; };

View File

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

View File

@@ -14,18 +14,58 @@
* Module dependencies. * Module dependencies.
*/ */
var urlparse = require('url').parse;
var urllib = require('urllib'); var urllib = require('urllib');
var HttpAgent = require('agentkeepalive'); var HttpAgent = require('agentkeepalive');
var HttpsAgent = require('agentkeepalive').HttpsAgent; var HttpsAgent = require('agentkeepalive').HttpsAgent;
var config = require('../config');
var httpAgent;
var httpsAgent;
if (config.httpProxy) {
var tunnel = require('tunnel-agent');
var urlinfo = urlparse(config.httpProxy);
if (urlinfo.protocol === 'http:') {
httpAgent = tunnel.httpOverHttp({
proxy: {
host: urlinfo.hostname,
port: urlinfo.port
}
});
httpsAgent = tunnel.httpsOverHttp({
proxy: {
host: urlinfo.hostname,
port: urlinfo.port
}
});
} else if (urlinfo.protocol === 'https:') {
httpAgent = tunnel.httpOverHttps({
proxy: {
host: urlinfo.hostname,
port: urlinfo.port
}
});
httpsAgent = tunnel.httpsOverHttps({
proxy: {
host: urlinfo.hostname,
port: urlinfo.port
}
});
} else {
throw new TypeError('httpProxy format error: ' + config.httpProxy);
}
} else {
httpAgent = new HttpAgent({
timeout: 0,
keepAliveTimeout: 15000
});
httpsAgent = new HttpsAgent({
timeout: 0,
keepAliveTimeout: 15000
});
}
var httpAgent = new HttpAgent({
timeout: 0,
keepAliveTimeout: 15000
});
var httpsAgent = new HttpsAgent({
timeout: 0,
keepAliveTimeout: 15000
});
var client = urllib.create({ var client = urllib.create({
agent: httpAgent, agent: httpAgent,
httpsAgent: httpsAgent httpsAgent: httpsAgent

View File

@@ -1,23 +1,9 @@
/**!
* cnpmjs.org - config/index.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com>
* fengmk2 <m@fengmk2.com> (http://fengmk2.com)
*/
'use strict'; 'use strict';
/**
* Module dependencies.
*/
var mkdirp = require('mkdirp'); var mkdirp = require('mkdirp');
var copy = require('copy-to'); var copy = require('copy-to');
var path = require('path'); var path = require('path');
var fs = require('fs');
var os = require('os'); var os = require('os');
var version = require('../package.json').version; var version = require('../package.json').version;
@@ -27,6 +13,7 @@ var dataDir = path.join(process.env.HOME || root, '.cnpmjs.org');
var config = { var config = {
version: version, version: version,
dataDir: dataDir,
/** /**
* Cluster mode * Cluster mode
@@ -84,6 +71,7 @@ var config = {
// email notification for errors // email notification for errors
// check https://github.com/andris9/Nodemailer for more informations // check https://github.com/andris9/Nodemailer for more informations
mail: { mail: {
enable: false,
appname: 'cnpmjs.org', appname: 'cnpmjs.org',
from: 'cnpmjs.org mail sender <adderss@gmail.com>', from: 'cnpmjs.org mail sender <adderss@gmail.com>',
service: 'gmail', service: 'gmail',
@@ -93,7 +81,8 @@ var config = {
} }
}, },
logoURL: '//ww4.sinaimg.cn/large/69c1d4acgw1ebfly5kjlij208202oglr.jpg', // cnpm logo image url logoURL: 'https://os.alipayobjects.com/rmsportal/oygxuIUkkrRccUz.jpg', // cnpm logo image url
adBanner: '',
customReadmeFile: '', // you can use your custom readme file instead the cnpm one customReadmeFile: '', // you can use your custom readme file instead the cnpm one
customFooter: '', // you can add copyright and site total script html here customFooter: '', // you can add copyright and site total script html here
npmClientName: 'cnpm', // use `${name} install package` npmClientName: 'cnpm', // use `${name} install package`
@@ -158,7 +147,7 @@ var config = {
enablePrivate: false, enablePrivate: false,
// registry scopes, if don't set, means do not support scopes // 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 // some registry already have some private packages in global scope
// but we want to treat them as scoped private packages, // but we want to treat them as scoped private packages,
@@ -173,12 +162,12 @@ var config = {
// cnpm wont directly sync from this one // cnpm wont directly sync from this one
// but sometimes will request it for some package infomations // but sometimes will request it for some package infomations
// please don't change it if not necessary // please don't change it if not necessary
officialNpmRegistry: 'https://registry.npmjs.org', officialNpmRegistry: 'https://registry.npmjs.com',
// sync source, upstream registry // sync source, upstream registry
// If you want to directly sync from official npm's registry // If you want to directly sync from official npm's registry
// please drop them an email first // please drop them an email first
sourceNpmRegistry: 'http://registry.npm.taobao.org', sourceNpmRegistry: 'https://registry.npm.taobao.org',
// upstream registry is base on cnpm/cnpmjs.org or not // upstream registry is base on cnpm/cnpmjs.org or not
// if your upstream is official npm registry, please turn it off // if your upstream is official npm registry, please turn it off
@@ -208,27 +197,43 @@ var config = {
// sync devDependencies or not, default is false // sync devDependencies or not, default is false
syncDevDependencies: false, syncDevDependencies: false,
// changes streaming sync
syncChangesStream: false,
handleSyncRegistry: 'http://127.0.0.1:7001',
// badge subject on http://shields.io/ // badge subject on http://shields.io/
badgePrefixURL: 'https://img.shields.io/badge',
badgeSubject: 'cnpm', badgeSubject: 'cnpm',
// custom user service, @see https://github.com/cnpm/cnpmjs.org/wiki/Use-Your-Own-User-Authorization // custom user service, @see https://github.com/cnpm/cnpmjs.org/wiki/Use-Your-Own-User-Authorization
// when you not intend to ingegrate with your company's user system, then use null, it would
// use the default cnpm user system
userService: null, userService: null,
// always-auth https://docs.npmjs.com/misc/config#always-auth
// Force npm to always require authentication when accessing the registry, even for GET requests.
alwaysAuth: false,
// if you're behind firewall, need to request through http proxy, please set this
// e.g.: `httpProxy: 'http://proxy.mycompany.com:8080'`
httpProxy: null,
}; };
if (process.env.NODE_ENV !== 'test') { if (process.env.NODE_ENV !== 'test') {
// 1. try to load `$dataDir/config.js` first, not exists then goto 2. var customConfig;
// 2. load config/config.js, everything in config.js will cover the same key in index.js if (process.env.NODE_ENV === 'development') {
var customConfig = path.join(dataDir, 'config'); customConfig = path.join(root, 'config', 'config.js');
try { } else {
copy(require(customConfig)).override(config); // 1. try to load `$dataDir/config.json` first, not exists then goto 2.
} catch (err) { // 2. load config/config.js, everything in config.js will cover the same key in index.js
customConfig = path.join(root, 'config', 'config'); customConfig = path.join(dataDir, 'config.json');
try { if (!fs.existsSync(customConfig)) {
copy(require(customConfig)).override(config); customConfig = path.join(root, 'config', 'config.js');
} catch (err) {
// ignore
} }
} }
if (fs.existsSync(customConfig)) {
copy(require(customConfig)).override(config);
}
} }
mkdirp.sync(config.logdir); mkdirp.sync(config.logdir);

View File

@@ -1,11 +1,9 @@
/**! /**!
* cnpmjs.org - controllers/registry/package/dist_tag.js * Copyright(c) cnpm and other contributors.
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -25,7 +23,7 @@ function ok() {
// GET /-/package/:pkg/dist-tags -- returns the package's dist-tags // GET /-/package/:pkg/dist-tags -- returns the package's dist-tags
exports.index = function* () { exports.index = function* () {
var name = this.params.name || this.params[0]; var name = this.params.name || this.params[0];
var rows = yield* packageService.listModuleTags(name); var rows = yield packageService.listModuleTags(name);
var tags = {}; var tags = {};
for (var i = 0; i < rows.length; i++) { for (var i = 0; i < rows.length; i++) {
var row = rows[i]; var row = rows[i];
@@ -79,6 +77,14 @@ exports.set = function* () {
exports.destroy = function* () { exports.destroy = function* () {
var name = this.params.name || this.params[0]; var name = this.params.name || this.params[0];
var tag = this.params.tag || this.params[1]; var tag = this.params.tag || this.params[1];
if (tag === 'latest') {
this.status = 400;
this.body = {
error: 'dist_tag_error',
reason: 'Can\'t not delete latest tag',
};
return;
}
yield packageService.removeModuleTagsByNames(name, tag); yield packageService.removeModuleTagsByNames(name, tag);
this.body = ok(); this.body = ok();
}; };

View File

@@ -1,11 +1,9 @@
/**! /**!
* cnpmjs.org - controllers/registry/package/download.js * Copyright(c) cnpm and other contributors.
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -18,6 +16,7 @@ var debug = require('debug')('cnpmjs.org:controllers:registry:download');
var mime = require('mime'); var mime = require('mime');
var utility = require('utility'); var utility = require('utility');
var defer = require('co-defer'); var defer = require('co-defer');
var is = require('is-type-of');
var nfs = require('../../../common/nfs'); var nfs = require('../../../common/nfs');
var logger = require('../../../common/logger'); var logger = require('../../../common/logger');
var common = require('../../../lib/common'); var common = require('../../../lib/common');
@@ -32,12 +31,16 @@ module.exports = function* download(next) {
var name = this.params.name || this.params[0]; var name = this.params.name || this.params[0];
var filename = this.params.filename || this.params[1]; var filename = this.params.filename || this.params[1];
var version = filename.slice(name.length + 1, -4); 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 // can not get dist
var url = null; var url = null;
if (typeof nfs.url === 'function') { 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); debug('download %s %s %s %s', name, filename, version, url);
@@ -52,10 +55,11 @@ module.exports = function* download(next) {
return; return;
} }
_downloads[name] = (_downloads[name] || 0) + 1;
if (config.downloadRedirectToNFS && url) { if (config.downloadRedirectToNFS && url) {
this.status = 302; this.status = 302;
this.set('Location', url); this.set('Location', url);
_downloads[name] = (_downloads[name] || 0) + 1;
return; return;
} }
@@ -66,17 +70,10 @@ module.exports = function* download(next) {
debug('get tarball by 302, url: %s', url); debug('get tarball by 302, url: %s', url);
this.status = 302; this.status = 302;
this.set('Location', url); this.set('Location', url);
_downloads[name] = (_downloads[name] || 0) + 1;
return; return;
} }
// else use `dist.key` to get tarball from nfs // else use `dist.key` to get tarball from nfs
if (!nfs.download) {
return yield* next;
}
_downloads[name] = (_downloads[name] || 0) + 1;
if (typeof dist.size === 'number' && dist.size > 0) { if (typeof dist.size === 'number' && dist.size > 0) {
this.length = dist.size; this.length = dist.size;
} }
@@ -84,7 +81,7 @@ module.exports = function* download(next) {
this.attachment(filename); this.attachment(filename);
this.etag = dist.shasum; this.etag = dist.shasum;
this.body = yield* downloadAsReadStream(dist.key); this.body = yield downloadAsReadStream(dist.key);
}; };
defer.setInterval(function* () { defer.setInterval(function* () {
@@ -110,10 +107,12 @@ defer.setInterval(function* () {
try { try {
yield* downloadTotalService.plusModuleTotal({ name: name, date: date, count: count }); yield* downloadTotalService.plusModuleTotal({ name: name, date: date, count: count });
} catch (err) { } catch (err) {
err.message += '; name: ' + name + ', count: ' + count + ', date: ' + date; if (err.name !== 'SequelizeUniqueConstraintError') {
logger.error(err); err.message += '; name: ' + name + ', count: ' + count + ', date: ' + date;
logger.error(err);
}
// save back to _downloads, try again next time // save back to _downloads, try again next time
_downloads[name] = (_downloads[name] || 0) + count; _downloads[name] = (_downloads[name] || 0) + count;
} }
} }
}, 5000); }, 5000 + Math.ceil(Math.random() * 1000));

View File

@@ -1,13 +1,3 @@
/**!
* cnpmjs.org - controllers/registry/package/list.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict'; 'use strict';
/** /**
@@ -45,9 +35,6 @@ module.exports = function* list() {
} }
} }
// use modifiedTime as etag
this.set('ETag', '"' + modifiedTime.getTime() + '"');
// must set status first // must set status first
this.status = 200; this.status = 200;
if (this.fresh) { if (this.fresh) {
@@ -104,7 +91,7 @@ module.exports = function* list() {
this.status = 404; this.status = 404;
this.body = { this.body = {
error: 'not_found', error: 'not_found',
reason: 'document not found' reason: 'document not found',
}; };
return; return;
} }
@@ -133,6 +120,10 @@ module.exports = function* list() {
for (var i = 0; i < rows.length; i++) { for (var i = 0; i < rows.length; i++) {
var row = rows[i]; var row = rows[i];
var pkg = row.package; var pkg = row.package;
// pkg is string ... ignore it
if (typeof pkg === 'string') {
continue;
}
common.setDownloadURL(pkg, this); common.setDownloadURL(pkg, this);
pkg._cnpm_publish_time = row.publish_time; pkg._cnpm_publish_time = row.publish_time;
versions[pkg.version] = pkg; versions[pkg.version] = pkg;
@@ -199,5 +190,5 @@ module.exports = function* list() {
info.license = pkg.license; info.license = pkg.license;
debug('show module %s: %s, latest: %s', orginalName, rev, latestMod.version); debug('show module %s: %s, latest: %s', orginalName, rev, latestMod.version);
this.body = info; this.jsonp = info;
}; };

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

@@ -0,0 +1,26 @@
/**!
* list package's dependents
*
* 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 name = this.params.name || this.params[0];
const dependents = yield packageService.listDependents(name);
this.body = {
dependents: dependents,
};
};

View File

@@ -1,11 +1,9 @@
/**! /**!
* cnpmjs.org - controllers/registry/package/list_shorts.js * Copyright(c) cnpm and other contributors.
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -19,5 +17,5 @@ var packageService = require('../../../services/package');
// GET /-/short // GET /-/short
// List all packages names only // List all packages names only
module.exports = function* () { module.exports = function* () {
this.body = yield* packageService.listAllPublicModuleNames(); this.body = yield packageService.listAllPublicModuleNames();
}; };

View File

@@ -1,11 +1,9 @@
/**! /**!
* cnpmjs.js - controllers/registry/package/show.js * Copyright(c) cnpm and other contributors.
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -32,13 +30,20 @@ var config = require('../../../config');
module.exports = function* show() { module.exports = function* show() {
var name = this.params.name || this.params[0]; var name = this.params.name || this.params[0];
var tag = this.params.version || this.params[1]; var tag = this.params.version || this.params[1];
if (tag === '*') {
tag = 'latest';
}
var version = semver.valid(tag); var version = semver.valid(tag);
var range = semver.validRange(tag);
var mod; var mod;
if (version) { if (version) {
mod = yield* packageService.getModule(name, version); mod = yield packageService.getModule(name, version);
} else if (range) {
mod = yield packageService.getModuleByRange(name, range);
} else { } else {
mod = yield* packageService.getModuleByTag(name, tag); mod = yield packageService.getModuleByTag(name, tag);
} }
if (mod) { if (mod) {
setDownloadURL(mod.package, this); setDownloadURL(mod.package, this);
mod.package._cnpm_publish_time = mod.publish_time; mod.package._cnpm_publish_time = mod.publish_time;
@@ -46,14 +51,14 @@ module.exports = function* show() {
if (maintainers.length > 0) { if (maintainers.length > 0) {
mod.package.maintainers = maintainers; mod.package.maintainers = maintainers;
} }
this.body = mod.package; this.jsonp = mod.package;
return; return;
} }
// if not fond, sync from source registry // if not fond, sync from source registry
if (!this.allowSync) { if (!this.allowSync) {
this.status = 404; this.status = 404;
this.body = { this.jsonp = {
error: 'not exist', error: 'not exist',
reason: 'version not found: ' + version reason: 'version not found: ' + version
}; };
@@ -61,7 +66,7 @@ module.exports = function* show() {
} }
// start sync // start sync
var logId = yield* SyncModuleWorker.sync(name, 'sync-by-install'); var logId = yield SyncModuleWorker.sync(name, 'sync-by-install');
debug('start sync %s, get log id %s', name, logId); debug('start sync %s, get log id %s', name, logId);
this.redirect(config.officialNpmRegistry + this.url); this.redirect(config.officialNpmRegistry + this.url);

View File

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

View File

@@ -1,21 +1,6 @@
/**!
* 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)
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
'use strict'; 'use strict';
/** var debug = require('debug')('cnpmjs.org:sync_module_worker');
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:proxy:sync_module_worker');
var co = require('co'); var co = require('co');
var gather = require('co-gather'); var gather = require('co-gather');
var defer = require('co-defer'); var defer = require('co-defer');
@@ -40,7 +25,11 @@ var User = require('../models').User;
var os = require('os'); var os = require('os');
var USER_AGENT = 'sync.cnpmjs.org/' + config.version + var USER_AGENT = 'sync.cnpmjs.org/' + config.version +
' hostname/' + os.hostname() + ' ' + urllib.USER_AGENT; ' hostname/' + os.hostname() +
' syncModel/' + config.syncModel +
' syncInterval/' + config.syncInterval +
' syncConcurrency/' + config.syncConcurrency +
' ' + urllib.USER_AGENT;
function SyncModuleWorker(options) { function SyncModuleWorker(options) {
EventEmitter.call(this); EventEmitter.call(this);
@@ -70,6 +59,7 @@ function SyncModuleWorker(options) {
this.successes = []; this.successes = [];
this.fails = []; this.fails = [];
this.updates = [];
} }
util.inherits(SyncModuleWorker, EventEmitter); util.inherits(SyncModuleWorker, EventEmitter);
@@ -116,10 +106,13 @@ SyncModuleWorker.prototype._saveLog = function () {
that._log = ''; that._log = '';
co(function* () { co(function* () {
yield* logService.append(that._logId, logstr); yield* logService.append(that._logId, logstr);
})(function (err) { }).then(function () {
if (err) { that._loging = false;
logger.error(err); if (that._log) {
that._saveLog();
} }
}).catch(function (err) {
logger.error(err);
that._loging = false; that._loging = false;
if (that._log) { if (that._log) {
that._saveLog(); that._saveLog();
@@ -131,11 +124,11 @@ SyncModuleWorker.prototype.start = function () {
this.log('user: %s, sync %s worker start, %d concurrency, nodeps: %s, publish: %s', this.log('user: %s, sync %s worker start, %d concurrency, nodeps: %s, publish: %s',
this.username, this.names[0], this.concurrency, this.noDep, this._publish); this.username, this.names[0], this.concurrency, this.noDep, this._publish);
var that = this; var that = this;
co(function *() { co(function* () {
// sync upstream // sync upstream
if (that.syncUpstreamFirst) { if (that.syncUpstreamFirst) {
try { try {
yield* that.syncUpstream(that.startName); yield that.syncUpstream(that.startName);
} catch (err) { } catch (err) {
logger.error(err); logger.error(err);
} }
@@ -151,7 +144,9 @@ SyncModuleWorker.prototype.start = function () {
arr.push(that.next(i)); arr.push(that.next(i));
} }
yield arr; yield arr;
})(); }).catch(function (err) {
logger.error(err);
});
}; };
SyncModuleWorker.prototype.pushSuccess = function (name) { SyncModuleWorker.prototype.pushSuccess = function (name) {
@@ -186,11 +181,16 @@ SyncModuleWorker.prototype._doneOne = function* (concurrencyId, name, success) {
var that = this; var that = this;
// relase the stack: https://github.com/cnpm/cnpmjs.org/issues/328 // relase the stack: https://github.com/cnpm/cnpmjs.org/issues/328
defer.setImmediate(function* () { defer.setImmediate(function* () {
yield* that.next(concurrencyId); yield that.next(concurrencyId);
}); });
}; };
SyncModuleWorker.prototype.syncUpstream = function* (name) { 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; var syncname = name;
if (this.type === 'user') { if (this.type === 'user') {
syncname = this.type + ':' + syncname; syncname = this.type + ':' + syncname;
@@ -280,6 +280,8 @@ SyncModuleWorker.prototype.syncUser = function* () {
SyncModuleWorker.prototype.next = function* (concurrencyId) { SyncModuleWorker.prototype.next = function* (concurrencyId) {
if (config.syncModel === 'none') { if (config.syncModel === 'none') {
this.log('[c#%d] [%s] syncModel is none, ignore',
concurrencyId, name);
return this.finish(); return this.finish();
} }
@@ -295,17 +297,17 @@ SyncModuleWorker.prototype.next = function* (concurrencyId) {
this.log('----------------- Syncing %s -------------------', name); this.log('----------------- Syncing %s -------------------', name);
// ignore scoped package // ignore private scoped package
if (name[0] === '@') { if (common.isPrivateScopedPackage(name)) {
this.log('[c#%d] [%s] ignore sync scoped package', this.log('[c#%d] [%s] ignore sync private scoped %j package',
concurrencyId, name); concurrencyId, name, config.scopes);
yield* this._doneOne(concurrencyId, name, true); yield this._doneOne(concurrencyId, name, true);
return; return;
} }
// get from npm // get from npm
try { try {
var result = yield* npmSerivce.request('/' + name); var result = yield npmSerivce.request('/' + name.replace('/', '%2f'));
pkg = result.data; pkg = result.data;
status = result.status; status = result.status;
} catch (err) { } catch (err) {
@@ -322,7 +324,7 @@ SyncModuleWorker.prototype.next = function* (concurrencyId) {
var unpublishedInfo = null; var unpublishedInfo = null;
if (status === 404) { if (status === 404) {
// check if it's unpublished // 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; unpublishedInfo = pkg.time.unpublished;
} else { } else {
pkg = null; pkg = null;
@@ -332,7 +334,7 @@ SyncModuleWorker.prototype.next = function* (concurrencyId) {
if (!pkg) { if (!pkg) {
that.log('[c#%s] [error] [%s] get package error: package not exists, status: %s', that.log('[c#%s] [error] [%s] get package error: package not exists, status: %s',
concurrencyId, name, status); concurrencyId, name, status);
yield* that._doneOne(concurrencyId, name, true); yield that._doneOne(concurrencyId, name, true);
return; return;
} }
@@ -340,31 +342,36 @@ SyncModuleWorker.prototype.next = function* (concurrencyId) {
if (unpublishedInfo) { if (unpublishedInfo) {
try { try {
yield* that._unpublished(name, unpublishedInfo); yield that._unpublished(name, unpublishedInfo);
} catch (err) { } catch (err) {
that.log('[c#%s] [error] [%s] sync error: %s', concurrencyId, name, err.stack); that.log('[c#%s] [error] [%s] sync error: %s', concurrencyId, name, err.stack);
yield* that._doneOne(concurrencyId, name, false); yield that._doneOne(concurrencyId, name, false);
return; return;
} }
return yield* that._doneOne(concurrencyId, name, true); return yield that._doneOne(concurrencyId, name, true);
} }
var versions; var versions;
try { try {
versions = yield* that._sync(name, pkg); versions = yield that._sync(name, pkg);
} catch (err) { } catch (err) {
that.log('[c#%s] [error] [%s] sync error: %s', concurrencyId, name, err.stack); that.log('[c#%s] [error] [%s] sync error: %s', concurrencyId, name, err.stack);
yield* that._doneOne(concurrencyId, name, false); yield that._doneOne(concurrencyId, name, false);
return; return;
} }
// has new version
if (versions.length > 0) {
that.updates.push(name);
}
this.log('[c#%d] [%s] synced success, %d versions: %s', this.log('[c#%d] [%s] synced success, %d versions: %s',
concurrencyId, name, versions.length, versions.join(', ')); concurrencyId, name, versions.length, versions.join(', '));
yield* this._doneOne(concurrencyId, name, true); yield this._doneOne(concurrencyId, name, true);
}; };
function* _listStarUsers(modName) { function* _listStarUsers(modName) {
var users = yield* packageService.listStarUserNames(modName); var users = yield packageService.listStarUserNames(modName);
var userMap = {}; var userMap = {};
users.forEach(function (user) { users.forEach(function (user) {
userMap[user] = true; userMap[user] = true;
@@ -373,11 +380,21 @@ function* _listStarUsers(modName) {
} }
function* _saveNpmUser(username) { function* _saveNpmUser(username) {
var user = yield* npmSerivce.getUser(username); var user = yield npmSerivce.getUser(username);
if (!user) { if (!user) {
return; var existsUser = yield User.findByName(username);
if (existsUser && existsUser.isNpmUser) {
// delete it
yield User.destroy({
where: {
name: username,
}
});
return { exists: true, deleted: true, isNpmUser: true };
}
return { exists: false };
} }
yield* User.saveNpmUser(user); yield User.saveNpmUser(user);
return user; return user;
} }
@@ -652,7 +669,7 @@ SyncModuleWorker.prototype._sync = function* (name, pkg) {
continue; continue;
} }
try { try {
yield* that._syncOneVersion(index, syncModule); yield that._syncOneVersion(index, syncModule);
syncedVersionNames.push(syncModule.version); syncedVersionNames.push(syncModule.version);
} catch (err) { } catch (err) {
that.log(' [%s:%d] sync error, version: %s, %s: %s', that.log(' [%s:%d] sync error, version: %s, %s: %s',
@@ -667,7 +684,7 @@ SyncModuleWorker.prototype._sync = function* (name, pkg) {
that.log(' [%s] %d versions: %j need to deleted', that.log(' [%s] %d versions: %j need to deleted',
name, deletedVersionNames.length, deletedVersionNames); name, deletedVersionNames.length, deletedVersionNames);
try { try {
yield* packageService.removeModulesByNameAndVersions(name, deletedVersionNames); yield packageService.removeModulesByNameAndVersions(name, deletedVersionNames);
} catch (err) { } catch (err) {
that.log(' [%s] delete error, %s: %s', name, err.name, err.message); that.log(' [%s] delete error, %s: %s', name, err.name, err.message);
} }
@@ -862,6 +879,7 @@ SyncModuleWorker.prototype._sync = function* (name, pkg) {
}; };
SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePackage) { SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePackage) {
logger.syncInfo('[sync_module_worker] start sync %s@%s', sourcePackage.name, sourcePackage.version);
var that = this; var that = this;
var username = this.username; var username = this.username;
var downurl = sourcePackage.dist.tarball; var downurl = sourcePackage.dist.tarball;
@@ -911,13 +929,14 @@ SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePack
} }
// add module dependence // add module dependence
yield* packageService.addDependencies(sourcePackage.name, dependencies); yield packageService.addDependencies(sourcePackage.name, dependencies);
var shasum = crypto.createHash('sha1'); var shasum = crypto.createHash('sha1');
var dataSize = 0; var dataSize = 0;
try { try {
// get tarball // get tarball
logger.syncInfo('[sync_module_worker] downloading %j to %j', downurl, filepath);
var r = yield urllib.request(downurl, options); var r = yield urllib.request(downurl, options);
var statusCode = r.status || -1; var statusCode = r.status || -1;
// https://github.com/cnpm/cnpmjs.org/issues/325 // https://github.com/cnpm/cnpmjs.org/issues/325
@@ -932,6 +951,7 @@ SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePack
var err = new Error('Download ' + downurl + ' fail, status: ' + statusCode); var err = new Error('Download ' + downurl + ' fail, status: ' + statusCode);
err.name = 'DownloadTarballError'; err.name = 'DownloadTarballError';
err.data = sourcePackage; err.data = sourcePackage;
logger.syncInfo('[sync_module_worker] %s', err.message);
throw err; throw err;
} }
@@ -948,6 +968,7 @@ SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePack
var err = new Error('Download ' + downurl + ' file size is zero'); var err = new Error('Download ' + downurl + ' file size is zero');
err.name = 'DownloadTarballSizeZeroError'; err.name = 'DownloadTarballSizeZeroError';
err.data = sourcePackage; err.data = sourcePackage;
logger.syncInfo('[sync_module_worker] %s', err.message);
throw err; throw err;
} }
@@ -958,6 +979,7 @@ SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePack
' not match ' + sourcePackage.dist.shasum); ' not match ' + sourcePackage.dist.shasum);
err.name = 'DownloadTarballShasumError'; err.name = 'DownloadTarballShasumError';
err.data = sourcePackage; err.data = sourcePackage;
logger.syncInfo('[sync_module_worker] %s', err.message);
throw err; throw err;
} }
@@ -968,8 +990,18 @@ SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePack
}; };
// upload to NFS // upload to NFS
logger.syncInfo('[sync_module_worker] uploading %j to nfs', options); logger.syncInfo('[sync_module_worker] uploading %j to nfs', options);
var result = yield nfs.upload(filepath, options); var result;
return yield *afterUpload(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 { } finally {
// remove tmp file whatever // remove tmp file whatever
fs.unlink(filepath, utility.noop); fs.unlink(filepath, utility.noop);
@@ -1012,7 +1044,7 @@ SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePack
} }
mod.package.dist = dist; mod.package.dist = dist;
var r = yield* packageService.saveModule(mod); var r = yield packageService.saveModule(mod);
that.log(' [%s:%s] done, insertId: %s, author: %s, version: %s, ' that.log(' [%s:%s] done, insertId: %s, author: %s, version: %s, '
+ 'size: %d, publish_time: %j, publish on cnpm: %s', + 'size: %d, publish_time: %j, publish on cnpm: %s',
@@ -1028,7 +1060,7 @@ SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePack
SyncModuleWorker.sync = function* (name, username, options) { SyncModuleWorker.sync = function* (name, username, options) {
options = options || {}; options = options || {};
var result = yield* logService.create({name: name, username: username}); var result = yield logService.create({name: name, username: username});
var worker = new SyncModuleWorker({ var worker = new SyncModuleWorker({
logId: result.id, logId: result.id,
type: options.type, type: options.type,

View File

@@ -1,31 +1,23 @@
/**!
* cnpmjs.org - controllers/total.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
'use strict'; 'use strict';
/** const Total = require('../services/total');
* Module dependencies. const version = require('../package.json').version;
*/ const config = require('../config');
const getDownloadTotal = require('./utils').getDownloadTotal;
var Total = require('../services/total'); const startTime = '' + Date.now();
var version = require('../package.json').version; let cache = null;
var config = require('../config');
var getDownloadTotal = require('./utils').getDownloadTotal;
var startTime = '' + Date.now();
module.exports = function* showTotal() { module.exports = function* showTotal() {
var r = yield [Total.get(), getDownloadTotal()]; if (cache && Date.now() - cache.cache_time < 10000) {
var total = r[0]; // cache 10 seconds
var download = r[1]; this.body = cache;
return;
}
const r = yield [Total.get(), getDownloadTotal()];
const total = r[0];
const download = r[1];
total.download = download; total.download = download;
total.db_name = 'registry'; total.db_name = 'registry';
@@ -35,5 +27,8 @@ module.exports = function* showTotal() {
total.donate = 'https://www.gittip.com/fengmk2'; total.donate = 'https://www.gittip.com/fengmk2';
total.sync_model = config.syncModel; total.sync_model = config.syncModel;
cache = total;
total.cache_time = Date.now();
this.body = total; this.body = total;
}; };

View File

@@ -27,6 +27,11 @@ var config = require('../config');
var DOWNLOAD_TIMEOUT = ms('10m'); var DOWNLOAD_TIMEOUT = ms('10m');
exports.downloadAsReadStream = function* (key) { exports.downloadAsReadStream = function* (key) {
var options = { timeout: DOWNLOAD_TIMEOUT };
if (nfs.createDownloadStream) {
return yield nfs.createDownloadStream(key, options);
}
var tmpPath = path.join(config.uploadDir, var tmpPath = path.join(config.uploadDir,
utility.randomString() + key.replace(/\//g, '-')); utility.randomString() + key.replace(/\//g, '-'));
function cleanup() { function cleanup() {
@@ -35,7 +40,7 @@ exports.downloadAsReadStream = function* (key) {
} }
debug('downloadAsReadStream() %s to %s', key, tmpPath); debug('downloadAsReadStream() %s to %s', key, tmpPath);
try { try {
yield nfs.download(key, tmpPath, {timeout: DOWNLOAD_TIMEOUT}); yield nfs.download(key, tmpPath, options);
} catch (err) { } catch (err) {
debug('downloadAsReadStream() %s to %s error: %s', key, tmpPath, err.stack); debug('downloadAsReadStream() %s to %s error: %s', key, tmpPath, err.stack);
cleanup(); cleanup();
@@ -51,11 +56,11 @@ exports.getDownloadTotal = function* (name) {
var end = moment(); var end = moment();
var start = end.clone().subtract(1, 'months').startOf('month'); var start = end.clone().subtract(1, 'months').startOf('month');
var lastday = end.clone().subtract(1, 'days').format('YYYY-MM-DD'); var lastday = end.clone().subtract(1, 'days').format('YYYY-MM-DD');
var lastweekStart = end.clone().subtract(1, 'weeks').startOf('week'); var lastweekStart = end.clone().subtract(1, 'weeks').startOf('isoweek');
var lastweekEnd = lastweekStart.clone().endOf('week').format('YYYY-MM-DD'); var lastweekEnd = lastweekStart.clone().endOf('isoweek').format('YYYY-MM-DD');
var lastmonthEnd = start.clone().endOf('month').format('YYYY-MM-DD'); var lastmonthEnd = start.clone().endOf('month').format('YYYY-MM-DD');
var thismonthStart = end.clone().startOf('month').format('YYYY-MM-DD'); var thismonthStart = end.clone().startOf('month').format('YYYY-MM-DD');
var thisweekStart = end.clone().startOf('week').format('YYYY-MM-DD'); var thisweekStart = end.clone().startOf('isoweek').format('YYYY-MM-DD');
start = start.format('YYYY-MM-DD'); start = start.format('YYYY-MM-DD');
end = end.format('YYYY-MM-DD'); end = end.format('YYYY-MM-DD');
lastweekStart = lastweekStart.format('YYYY-MM-DD'); lastweekStart = lastweekStart.format('YYYY-MM-DD');

View File

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

View File

@@ -6,7 +6,7 @@
* *
* Authors: * Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me) * dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <m@fengmk2.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -35,13 +35,12 @@ module.exports = function* search() {
// return a json result // return a json result
if (this.query && this.query.type === 'json') { if (this.query && this.query.type === 'json') {
this.body = { this.jsonp = {
keyword: word, keyword: word,
match: match, match: match,
packages: result.searchMatchs, packages: result.searchMatchs,
keywords: result.keywordMatchs, keywords: result.keywordMatchs,
}; };
this.type = 'application/json; charset=utf-8';
return; return;
} }
yield this.render('search', { yield this.render('search', {

View File

@@ -1,20 +1,5 @@
/**!
* cnpmjs.org - controllers/web/package/show.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict'; 'use strict';
/**
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:controllers:web:package:show'); var debug = require('debug')('cnpmjs.org:controllers:web:package:show');
var bytes = require('bytes'); var bytes = require('bytes');
var giturl = require('giturl'); var giturl = require('giturl');
@@ -68,7 +53,8 @@ module.exports = function* show(next) {
} }
} }
yield this.render('package_unpublished', { yield this.render('package_unpublished', {
package: data package: data,
title: 'Package - ' + name
}); });
return; return;
} }
@@ -178,12 +164,23 @@ module.exports = function* show(next) {
pkg.engines[k] = { pkg.engines[k] = {
version: engine, version: engine,
title: k + ': ' + engine, title: k + ': ' + engine,
badgeURL: 'https://img.shields.io/badge/' + encodeURIComponent(k) + badgeURL: config.badgePrefixURL + '/' + encodeURIComponent(k) +
'-' + encodeURIComponent(engine) + '-' + color + '.svg?style=flat-square', '-' + encodeURIComponent(engine) + '-' + color + '.svg?style=flat-square',
}; };
} }
} }
if (pkg._publish_on_cnpm) {
pkg.isPrivate = true;
} else {
pkg.isPrivate = false;
// add security check badge
pkg.snyk = {
badge: `https://snyk.io/test/npm/${pkg.name}/badge.svg?style=flat-square`,
url: `https://snyk.io/test/npm/${pkg.name}`,
};
}
yield this.render('package', { yield this.render('package', {
title: 'Package - ' + pkg.name, title: 'Package - ' + pkg.name,
package: pkg, package: pkg,

View File

@@ -37,7 +37,8 @@ module.exports = function* showUser(next) {
var data = { var data = {
name: name, name: name,
email: user.email, email: user.email,
json: user.json || {} json: user.json || {},
isNpmUser: user.isNpmUser,
}; };
if (data.json.login) { if (data.json.login) {

View File

@@ -1,20 +1,5 @@
/**!
* 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)
*/
'use strict'; 'use strict';
/**
* Module dependencies.
*/
var childProcess = require('child_process'); var childProcess = require('child_process');
var path = require('path'); var path = require('path');
var util = require('util'); var util = require('util');
@@ -23,8 +8,8 @@ var config = require('./config');
var workerPath = path.join(__dirname, 'worker.js'); var workerPath = path.join(__dirname, 'worker.js');
var syncPath = path.join(__dirname, 'sync'); var syncPath = path.join(__dirname, 'sync');
console.log('Starting cnpmjs.org ...\ncluster: %s\nadmins: %j\nscopes: %j\nsourceNpmRegistry: %s', console.log('Starting cnpmjs.org ...\ncluster: %s\nadmins: %j\nscopes: %j\nsourceNpmRegistry: %s\nsyncModel: %s',
config.enableCluster, config.admins, config.scopes, config.sourceNpmRegistry); config.enableCluster, config.admins, config.scopes, config.sourceNpmRegistry, config.syncModel);
if (config.enableCluster) { if (config.enableCluster) {
forkWorker(); forkWorker();

BIN
docs/network.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

98
docs/network.puml Normal file
View File

@@ -0,0 +1,98 @@
@startuml
title cnpmjs.org, npm.taobao.org Network
node "China User" {
[cnpm cli]
}
node "OSS: aliyuncs.com" {
[tnpm-hz.oss-cn-hangzhou]
}
node "SLB: 114.55.80.225 Hangzhou" {
[npm.taobao.org]
[registry.npm.taobao.org]
}
node "npmtaobao3: 121.41.*" {
[nginx 5]
[registry 5]
[web 5]
[syncer 5]
}
node "npmtaobao4: 120.26.*" {
[nginx 6]
[registry 6]
[web 6]
}
node "cnpm6: 47.88.189.193 Singapore" {
[cnpmjs.org]
[r.cnpmjs.org]
[nginx 7]
[registry 7]
[web 7]
[syncer 7]
}
node "Aliyun CDN" {
[cdn.npm.taobao.org]
}
node "qiniu CDN" {
[cnpmjs.up.qiniu.com]
[dn-cnpm.qbox.me]
}
database "taobaonpm DB" {
[rds2860*.mysql.rds.aliyuncs.com]
}
database "cnpm DB" {
[rdsqiz*.mysql.rds.aliyuncs.com]
}
node "npmjs.com" {
[registry.npmjs.com]
[www.npmjs.com]
}
[cnpm cli] -down-> [registry.npm.taobao.org]: Read, Write
[cnpm cli] -down-> [cdn.npm.taobao.org]: Read tgz
[cnpm cli] -down-> [npm.taobao.org]: "Read /mirrors"
[registry.npm.taobao.org] -down-> [nginx 5]
[nginx 5] -down-> [registry 5]
[npm.taobao.org] -down-> [nginx 5]
[nginx 5] -down-> [web 5]
[registry.npm.taobao.org] -down-> [nginx 6]
[nginx 6] -down-> [registry 6]
[npm.taobao.org] -down-> [nginx 6]
[nginx 6] -down-> [web 6]
[registry 5] -down-> [rds2860*.mysql.rds.aliyuncs.com]: Read, Write
[web 5] -down-> [rds2860*.mysql.rds.aliyuncs.com]: Read
[syncer 5] -down-> [rds2860*.mysql.rds.aliyuncs.com]: Read, Write
[syncer 5] -down-> [tnpm-hz.oss-cn-hangzhou]: Write
[syncer 5] -> [r.cnpmjs.org]: Read
[syncer 5] -> [dn-cnpm.qbox.me]: Read tgz
[registry 6] -down-> [rds2860*.mysql.rds.aliyuncs.com]: Read, Write
[web 6] -down-> [rds2860*.mysql.rds.aliyuncs.com]: Read
[cnpmjs.org] -down-> [nginx 7]
[nginx 7] -down-> [registry 7]
[r.cnpmjs.org] -down-> [nginx 7]
[nginx 7] -down-> [web 7]
[registry 7] -down-> [rdsqiz*.mysql.rds.aliyuncs.com]: Read, Write
[web 7] -down-> [rdsqiz*.mysql.rds.aliyuncs.com]: Read
[syncer 7] -down-> [rdsqiz*.mysql.rds.aliyuncs.com]: Read, Write
[syncer 7] -down-> [cnpmjs.up.qiniu.com]: Write
[syncer 7] -down-> [registry.npmjs.com]: Read
[cdn.npm.taobao.org] -down-> [tnpm-hz.oss-cn-hangzhou]: Read
[dn-cnpm.qbox.me] -down-> [cnpmjs.up.qiniu.com]: Read
@enduml

View File

@@ -1,115 +0,0 @@
# Install & Get Started
## Deps
* MySQL Server: http://db4free.net/
* qiniu CDN: http://www.qiniu.com/
* redis session: https://garantiadata.com Support 24MB free spaces.
* node: >=0.10.21
## Clone
```bash
$ git clone git://github.com/fengmk2/cnpmjs.org.git $HOME/cnpmjs.org
$ cd $HOME/cnpmjs.org
```
## Create your `config.js`
```bash
$ vim config/config.js
```
`config.js` content sample:
```js
module.exports = {
debug: false,
enableCluster: true, // enable cluster mode
logdir: 'your application log dir',
mysqlServers: [
{
host: 'your mysql host',
port: 3306,
user: 'yourname',
password: 'your password'
}
],
mysqlDatabase: 'cnpmjs',
redis: {
host: 'your redist host',
port: 6379,
},
qn: {
accessKey: "your qiniu appkey",
secretKey: "your secret key",
bucket: "foobucket",
domain: "http://foobucket.u.qiniudn.com"
},
nfs: null, // you can set a nfs to replace qiniu cdn
enablePrivate: true, // enable private mode, only admin can publish, other use just can sync package from source npm
admins: {
admin: 'admin@cnpmjs.org',
},
syncModel: 'exist', //`all` sync all packages, `exist` only update exist packages, `none` do nothing
};
```
## Create MySQL Database and Tables
```bash
$ mysql -u yourname -p
mysql> use cnpmjs;
mysql> source docs/db.sql
```
## Use your own CDN
If you wan to use your own CDN instead of qiniu. Just look at `common/qnfs.js` and implement the interface like it, then pass it by set `config.nfs`.
## npm install
```bash
$ npm install
```
## start
```bash
$ ./bin/nodejsctl start
Starting cnpmjs.org ...
Start nodejs success. PID=27175
```
## open registry and web
```bash
# registry
$ open http://localhost:7001
# web
$ open http://localhost:7002
```
## use cnpm cli with your own registry
You do not need to write another command line tool with your own registry,
just alias [cnpm](http://github.com/fengmk2/cnpm), then you can get a npm client for you own registry.
```
# install cnpm first
npm install -g cnpm
# then alias lnpm to cnpm, but change config to your own registry
alias lnpm='cnpm --registry=http://localhost:7001\
--registryweb=http://localhost:7002\
--cache=$HOME/.npm/.cache/lnpm\
--disturl=http://cnpmjs.org/dist\
--userconfig=$HOME/.lnpmrc'
#or put this in .zshrc or .bashrc
echo "#lnpm alias\nalias lnpm='cnpm --registry=http://localhost:7001\
--registryweb=http://localhost:7002\
--cache=$HOME/.npm/.cache/lnpm\
--disturl=http://cnpmjs.org/dist\
--userconfig=$HOME/.lnpmrc'" >> $HOME/.zshrc && source $HOME/.zshrc
```

View File

@@ -4,11 +4,12 @@ So `cnpm` is meaning: **Company npm**.
## Registry ## Registry
- Our public registry: [r.cnpmjs.org](http://r.cnpmjs.org), syncing from [registry.npmjs.org](http://registry.npmjs.org) - Our public registry: [r.cnpmjs.org](https://r.cnpmjs.org), syncing from [registry.npmjs.com](https://registry.npmjs.com)
- [cnpmjs.org](/) version: <span id="app-version"></span> - [cnpmjs.org](/) version: <span id="app-version"></span>
- [Node.js](https://nodejs.org) version: <span id="node-version"></span> - [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/)。 - For developers in China, please visit [the China mirror](https://npm.taobao.org). 中国用户请访问[国内镜像站点](https://npm.taobao.org)。
<div class="ant-table">
<table class="downloads"> <table class="downloads">
<tbody> <tbody>
<tr> <tr>
@@ -28,12 +29,17 @@ So `cnpm` is meaning: **Company npm**.
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div>
<div class="sync" style="display:none;"> <div class="sync" style="display:none;">
<h3>Sync Status</h3> <h3>Sync Status</h3>
<p id="sync-model"></p> <p id="sync-model"></p>
<p>Last sync time is <span id="last-sync-time"></span>. </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 syncing">
<span class="anticon ant-alert-icon anticon-info-circle"></span>
<span class="ant-alert-description">The sync worker is working in the backend now. </span>
</div>
<div class="ant-table">
<table class="sync-status"> <table class="sync-status">
<tbody> <tbody>
<tr> <tr>
@@ -48,48 +54,53 @@ So `cnpm` is meaning: **Company npm**.
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div>
</div> </div>
Running on [Node.js](http://nodejs.org), version <span id="node-version"></span>.
<script src="/js/readme.js"></script> <script src="/js/readme.js"></script>
## Version Badge ## Badges
Default style is `flat-square`. Default style is `flat-square`.
Badge URL: `http://cnpmjs.org/badge/v/cnpmjs.org.svg` ![cnpmjs.org-badge](http://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) Badge URL: `https://cnpmjs.org/badge/v/cnpmjs.org.svg` ![cnpmjs.org-version-badge](//cnpmjs.org/badge/v/cnpmjs.org.svg)
* `<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) * `<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: `https://cnpmjs.org/badge/d/cnpmjs.org.svg` ![cnpmjs.org-download-badge](//cnpmjs.org/badge/d/cnpmjs.org.svg)
## Usage ## 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: 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 ```bash
$ npm install -g cnpm --registry=http://r.cnpmjs.org $ npm install -g cnpm --registry=https://registry.npm.taobao.org
``` ```
Or you can alias NPM to use it: Or you can alias NPM to use it:
```bash ```bash
alias cnpm="npm --registry=http://r.cnpmjs.org \ alias cnpm="npm --registry=https://registry.npm.taobao.org \
--cache=$HOME/.npm/.cache/cnpm \ --cache=$HOME/.npm/.cache/cnpm \
--disturl=http://cnpmjs.org/dist \ --disturl=https://npm.taobao.org/mirrors/node \
--userconfig=$HOME/.cnpmrc" --userconfig=$HOME/.cnpmrc"
#Or alias it in .bashrc or .zshrc #Or alias it in .bashrc or .zshrc
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=http://r.cnpmjs.org \ $ echo '\n#alias for cnpm\nalias cnpm="npm --registry=https://registry.npm.taobao.org \
--cache=$HOME/.npm/.cache/cnpm \ --cache=$HOME/.npm/.cache/cnpm \
--disturl=http://cnpmjs.org/dist \ --disturl=https://npm.taobao.org/mirrors/node \
--userconfig=$HOME/.cnpmrc"' >> ~/.zshrc && source ~/.zshrc --userconfig=$HOME/.cnpmrc"' >> ~/.zshrc && source ~/.zshrc
``` ```
### install ### install
Install package from [r.cnpmjs.org](http://r.cnpmjs.org). When installing a package or version does not exist, it will try to install from the official registry([registry.npmjs.org](http://registry.npmjs.org)), and sync this package to cnpm in the backend. Install package from [r.cnpmjs.org](//r.cnpmjs.org). When installing a package or version does not exist, it will try to install from the official registry([registry.npmjs.org](https://registry.npmjs.org)), and sync this package to cnpm in the backend.
```bash ```bash
$ cnpm install [name] $ cnpm install [name]
@@ -106,7 +117,7 @@ $ cnpm sync connect
sync package on web: [sync/connect](/sync/connect) sync package on web: [sync/connect](/sync/connect)
```bash ```bash
$ open http://cnpmjs.org/sync/connect $ open http://registry.npm.taobao.org/sync/connect
``` ```
### publish / unpublish ### publish / unpublish
@@ -134,6 +145,11 @@ $ cnpm info cnpm
Release [History](/history). Release [History](/history).
## npm and cnpm relation ## npmjs.org, cnpmjs.org and npm.taobao.org relation
![npm&cnpm](https://dn-cnpm.qbox.me/cnpm-npm-relation.png) [![npm&cnpm](https://dn-cnpm.qbox.me/network.png)](https://dn-cnpm.qbox.me/network.png)
## Sponsors
- [![阿里云](https://static.aliyun.com/images/www-summerwind/logo.gif)](http://click.aliyun.com/m/4288/) (2016.2 - now)
- [![UCloud云计算](https://www.ucloud.cn/static/style/images/about/logo.png)](http://www.ucloud.cn?sem=sdk-CNPMJS) (2015.3 - 2016.3)

View File

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

View File

@@ -27,6 +27,11 @@ exports.getTarballFilepath = function (filename) {
}; };
exports.getCDNKey = function (name, 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; return '/' + name + '/-/' + filename;
}; };
@@ -66,3 +71,10 @@ exports.isLocalModule = function (mods) {
} }
return false; return false;
}; };
exports.isPrivateScopedPackage = function (name) {
if (name[0] !== '@') {
return false;
}
return config.scopes.indexOf(name.split('/')[0]) >= 0;
};

View File

@@ -16,6 +16,7 @@
var debug = require('debug')('cnpmjs.org:middleware:auth'); var debug = require('debug')('cnpmjs.org:middleware:auth');
var UserService = require('../services/user'); var UserService = require('../services/user');
var config = require('../config');
/** /**
* Parse the request authorization * Parse the request authorization
@@ -30,12 +31,12 @@ module.exports = function () {
authorization = authorization.trim(); authorization = authorization.trim();
debug('%s %s with %j', this.method, this.url, authorization); debug('%s %s with %j', this.method, this.url, authorization);
if (!authorization) { if (!authorization) {
return yield* next; return yield* unauthorized.call(this, next);
} }
authorization = new Buffer(authorization, 'base64').toString().split(':'); authorization = new Buffer(authorization, 'base64').toString().split(':');
if (authorization.length !== 2) { if (authorization.length !== 2) {
return yield* next; return yield* unauthorized.call(this, next);
} }
var username = authorization[0]; var username = authorization[0];
@@ -52,7 +53,7 @@ module.exports = function () {
if (!row) { if (!row) {
debug('auth fail user: %j, headers: %j', row, this.header); debug('auth fail user: %j, headers: %j', row, this.header);
return yield* next; return yield* unauthorized.call(this, next);
} }
this.user.name = row.login; this.user.name = row.login;
@@ -62,3 +63,19 @@ module.exports = function () {
yield* next; yield* next;
}; };
}; };
function* unauthorized(next) {
if (!config.alwaysAuth || this.method !== 'GET') {
return yield* next;
}
this.status = 401;
this.set('WWW-Authenticate', 'Basic realm="sample"');
if (this.accepts(['html', 'json']) === 'json') {
this.body = {
error: 'unauthorized',
reason: 'login first'
};
} else {
this.body = 'login first';
}
}

View File

@@ -1,113 +0,0 @@
/**
* CORS middleware
*
* @param {Object} [settings]
* @return {Function}
* @api public
*/
module.exports = function(settings) {
"use strict";
var defaults = {
origin: function(ctx) {
return ctx.get('origin') || '*';
},
methods: 'GET,HEAD,PUT,POST,DELETE'
};
/**
* Set options
*
* @type {Object}
*/
var options = settings || defaults;
if (options.origin !== false) {
var t = typeof options.origin;
if (t !== 'string' && t !== 'function') {
options.origin = defaults.origin;
}
}
if (Array.isArray(options.expose)) {
options.expose = options.expose.join(',');
}
options.maxAge = options.maxAge && options.maxAge.toString();
options.methods = options.methods || defaults.methods;
if (Array.isArray(options.methods)) {
options.methods = options.methods.join(',');
}
if (Array.isArray(options.headers)) {
options.headers = options.headers.join(',');
}
return function* cors(next) {
/**
* Access Control Allow Origin
*/
var origin = options.origin;
if (origin === false) {
return yield* next;
}
if (typeof options.origin === 'function') {
origin = options.origin(this);
}
if (origin === false) {
return yield* next;
}
this.set('Access-Control-Allow-Origin', origin);
/**
* Access Control Expose Headers
*/
if (options.expose) {
this.set('Access-Control-Expose-Headers', options.expose);
}
/**
* Access Control Max Age
*/
if (options.maxAge) {
this.set('Access-Control-Max-Age', options.maxAge);
}
/**
* Access Control Allow Credentials
*/
if (options.credentials === true) {
this.set('Access-Control-Allow-Credentials', 'true');
}
/**
* Access Control Allow Methods
*/
this.set('Access-Control-Allow-Methods', options.methods);
/**
* Access Control Allow Headers
*/
var allowHeaders = options.headers;
if (!allowHeaders) {
allowHeaders = this.get('access-control-request-headers');
}
if (allowHeaders) {
this.set('Access-Control-Allow-Headers', allowHeaders);
}
/**
* Returns
*/
if (this.method === 'OPTIONS') {
this.status = 204;
} else {
yield* next;
}
};
};

View File

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

View File

@@ -22,7 +22,7 @@ var template = '<?xml version="1.0" encoding="UTF-8"?>\
<Url method="get" type="text/html" template="http://${host}/browse/keyword/{searchTerms}"/>\ <Url method="get" type="text/html" template="http://${host}/browse/keyword/{searchTerms}"/>\
</OpenSearchDescription>'; </OpenSearchDescription>';
module.exports = function *publishable(next) { module.exports = function *opensearch(next) {
if (this.path === '/opensearch.xml') { if (this.path === '/opensearch.xml') {
this.type = 'text/xml'; this.type = 'text/xml';
this.charset = 'utf-8'; this.charset = 'utf-8';

View File

@@ -1,12 +1,3 @@
/**!
* cnpmjs.org - middleware/proxy_to_npm.js
*
* Copyright(c) Alibaba Group Holding Limited.
*
* Authors:
* 苏千 <suqian.yf@alipay.com> (http://fengmk2.com)
*/
'use strict'; 'use strict';
/** /**
@@ -33,11 +24,11 @@ module.exports = function (options) {
} }
return function* proxyToNpm(next) { return function* proxyToNpm(next) {
if (config.syncModel !== 'none') { if (config.syncModel !== 'none') {
return yield* next; return yield next;
} }
// only proxy read requests // only proxy read requests
if (this.method !== 'GET' && this.method !== 'HEAD') { if (this.method !== 'GET' && this.method !== 'HEAD') {
return yield* next; return yield next;
} }
var pathname = this.path; var pathname = this.path;
@@ -49,7 +40,7 @@ module.exports = function (options) {
} }
} }
if (!match) { if (!match) {
return yield* next; return yield next;
} }
var url = redirectUrl + this.url; var url = redirectUrl + this.url;

View File

@@ -23,7 +23,8 @@ var staticDir = path.join(path.dirname(__dirname), 'public');
module.exports = middlewares.staticCache(staticDir, { module.exports = middlewares.staticCache(staticDir, {
buffer: config.debug ? false : true, buffer: config.debug ? false : true,
maxAge: config.debug ? 0 : 60 * 60 * 24 * 7, maxAge: config.debug ? 0 : 60 * 60 * 24 * 7,
alas: { alias: {
'/favicon.ico': '/favicon.png' '/favicon.ico': '/favicon.png'
} },
gzip: config.enableCompress,
}); });

View File

@@ -1,7 +1,5 @@
/**! /**
* cnpmjs.org - middleware/sync_by_install.js * Copyright(c) cnpm and other contributors.
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
@@ -17,33 +15,36 @@
var config = require('../config'); var config = require('../config');
/** /**
* this.allowSync - allow sync triggle by cnpm install * {Boolean} this.allowSync - allow sync triggle by cnpm install
*/ */
module.exports = function* syncByInstall(next) { module.exports = function* syncByInstall(next) {
this.allowSync = false; this.allowSync = false;
if (!config.syncByInstall) { if (!config.syncByInstall) {
// only config.enablePrivate should enable sync on install // only config.enablePrivate should enable sync on install
return yield* next; return yield next;
} }
// request not by node, consider it request from web, dont sync // request not by node, consider it request from web, don't sync
var ua = this.get('user-agent'); var ua = this.get('user-agent');
if (!ua || ua.indexOf('node') < 0) { if (!ua || ua.indexOf('node') < 0) {
return yield* next; return yield next;
} }
// if request with `/xxx?write=true`, meaning the read request using for write, dont sync // if request with `/xxx?write=true`, meaning the read request using for write, don't sync
if (this.query.write) { if (this.query.write) {
return yield* next; return yield next;
} }
var name = this.params.name || this.params[0]; var name = this.params.name || this.params[0];
// scoped package dont sync // private scoped package don't sync
if (name && name[0] === '@') { if (name && name[0] === '@') {
return yield* next; var scope = name.split('/')[0];
if (config.scopes.indexOf(scope) >= 0) {
return yield next;
}
} }
this.allowSync = true; this.allowSync = true;
yield* next; yield next;
}; };

View File

@@ -0,0 +1,24 @@
/**!
* cnpmjs.org - middleware/unpublishable.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com>
*/
'use strict';
module.exports = function *unpublishable(next) {
// only admin user can unpublish
if (!this.user.isAdmin) {
this.status = 403;
this.body = {
error: 'no_perms',
reason: 'Only administrators can unpublish module',
};
return;
}
yield next;
};

View File

@@ -64,192 +64,192 @@ module.exports = function (sequelize, DataTypes) {
comment: 'module name', comment: 'module name',
}, },
date: { date: {
type: DataTypes.INTEGER.UNSIGNED, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
comment: 'YYYYMM format', comment: 'YYYYMM format',
}, },
d01: { d01: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '01 download count', comment: '01 download count',
}, },
d02: { d02: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '02 download count', comment: '02 download count',
}, },
d03: { d03: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '03 download count', comment: '03 download count',
}, },
d04: { d04: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '04 download count', comment: '04 download count',
}, },
d05: { d05: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '05 download count', comment: '05 download count',
}, },
d06: { d06: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '06 download count', comment: '06 download count',
}, },
d07: { d07: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '07 download count', comment: '07 download count',
}, },
d08: { d08: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '08 download count', comment: '08 download count',
}, },
d09: { d09: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '09 download count', comment: '09 download count',
}, },
d10: { d10: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '10 download count', comment: '10 download count',
}, },
d11: { d11: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '11 download count', comment: '11 download count',
}, },
d12: { d12: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '12 download count', comment: '12 download count',
}, },
d13: { d13: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '13 download count', comment: '13 download count',
}, },
d14: { d14: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '14 download count', comment: '14 download count',
}, },
d15: { d15: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '15 download count', comment: '15 download count',
}, },
d16: { d16: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '16 download count', comment: '16 download count',
}, },
d17: { d17: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '17 download count', comment: '17 download count',
}, },
d18: { d18: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '18 download count', comment: '18 download count',
}, },
d19: { d19: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '19 download count', comment: '19 download count',
}, },
d20: { d20: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '20 download count', comment: '20 download count',
}, },
d21: { d21: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '21 download count', comment: '21 download count',
}, },
d22: { d22: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '22 download count', comment: '22 download count',
}, },
d23: { d23: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '23 download count', comment: '23 download count',
}, },
d24: { d24: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '24 download count', comment: '24 download count',
}, },
d25: { d25: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '25 download count', comment: '25 download count',
}, },
d26: { d26: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '26 download count', comment: '26 download count',
}, },
d27: { d27: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '27 download count', comment: '27 download count',
}, },
d28: { d28: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '28 download count', comment: '28 download count',
}, },
d29: { d29: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '29 download count', comment: '29 download count',
}, },
d30: { d30: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '30 download count', comment: '30 download count',
}, },
d31: { d31: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: '31 download count', comment: '31 download count',

View File

@@ -1,11 +1,9 @@
/**! /**!
* cnpmjs.org - models/index.js
*
* Copyright(c) fengmk2 and other contributors. * Copyright(c) fengmk2 and other contributors.
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -38,7 +36,12 @@ module.exports = {
DownloadTotal: load('download_total'), DownloadTotal: load('download_total'),
query: function* (sql, args) { query: function* (sql, args) {
return yield this.sequelize.query(sql, null, {raw: true}, args); var options = { replacements: args };
var data = yield this.sequelize.query(sql, options);
if (/select /i.test(sql)) {
return data[0];
}
return data[1];
}, },
queryOne: function* (sql, args) { queryOne: function* (sql, args) {
var rows = yield* this.query(sql, args); var rows = yield* this.query(sql, args);

View File

@@ -18,16 +18,27 @@ var config = require('../config');
config.database.logging = console.log; config.database.logging = console.log;
// $ node --harmony models/init_script.js <force> <dialect> // $ node --harmony models/init_script.js <force> <dialect> <port> <username>
var force = process.argv[2] === 'true'; var force = process.argv[2] === 'true';
var dialect = process.argv[3]; var dialect = process.argv[3];
if (dialect) { if (dialect) {
config.database.dialect = dialect; config.database.dialect = dialect;
} }
var port = process.argv[4];
if (port) {
config.database.port = parseInt(port);
}
var username = process.argv[5];
if (username) {
config.database.username = username;
}
var models = require('./'); var models = require('./');
models.sequelize.sync({ force: force }) models.sequelize.sync({
force: force,
logging: console.log,
})
.then(function () { .then(function () {
models.Total.init(function (err) { models.Total.init(function (err) {
if (err) { if (err) {
@@ -44,5 +55,5 @@ models.sequelize.sync({ force: force })
.catch(function (err) { .catch(function (err) {
console.error('[models/init_script.js] sequelize sync fail'); console.error('[models/init_script.js] sequelize sync fail');
console.error(err); console.error(err);
throw err; process.exit(1);
}); });

View File

@@ -14,8 +14,6 @@
* Module dependencies. * Module dependencies.
*/ */
var utils = require('./utils');
/* /*
CREATE TABLE IF NOT EXISTS `module` ( CREATE TABLE IF NOT EXISTS `module` (
`id` INTEGER NOT NULL auto_increment , `id` INTEGER NOT NULL auto_increment ,
@@ -73,12 +71,12 @@ module.exports = function (sequelize, DataTypes) {
allowNull: true, allowNull: true,
}, },
dist_size: { dist_size: {
type: DataTypes.INTEGER.UNSIGNED, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
}, },
publish_time: { publish_time: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: true, allowNull: true,
} }
}, { }, {

View File

@@ -70,7 +70,7 @@ module.exports = function (sequelize, DataTypes) {
}); });
if (row) { if (row) {
row.package = pkg; row.package = pkg;
if (row.isDirty) { if (row.changed()) {
row = yield row.save(['package']); row = yield row.save(['package']);
} }
return row; return row;

View File

@@ -47,7 +47,7 @@ module.exports = function (sequelize, DataTypes) {
comment: 'module version', comment: 'module version',
}, },
module_id: { module_id: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
comment: 'module id' comment: 'module id'
} }

View File

@@ -40,49 +40,49 @@ module.exports = function (sequelize, DataTypes) {
comment: 'total name' comment: 'total name'
}, },
module_delete: { module_delete: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: 'module delete count', comment: 'module delete count',
}, },
last_sync_time: { last_sync_time: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: 'last timestamp sync from official registry', comment: 'last timestamp sync from official registry',
}, },
last_exist_sync_time: { last_exist_sync_time: {
type: DataTypes.BIGINT(20).UNSIGNED, type: DataTypes.BIGINT(20),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: 'last timestamp sync exist packages from official registry', comment: 'last timestamp sync exist packages from official registry',
}, },
sync_status: { sync_status: {
type: 'TINYINT', type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: 'system sync from official registry status', comment: 'system sync from official registry status',
}, },
need_sync_num: { need_sync_num: {
type: DataTypes.INTEGER.UNSIGNED, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: 'how many packages need to be sync', comment: 'how many packages need to be sync',
}, },
success_sync_num: { success_sync_num: {
type: DataTypes.INTEGER.UNSIGNED, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: 'how many packages sync success at this time', comment: 'how many packages sync success at this time',
}, },
fail_sync_num: { fail_sync_num: {
type: DataTypes.INTEGER.UNSIGNED, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: 'how many packages sync fail at this time', comment: 'how many packages sync fail at this time',
}, },
left_sync_num: { left_sync_num: {
type: DataTypes.INTEGER.UNSIGNED, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
comment: 'how many packages left to be sync', comment: 'how many packages left to be sync',

View File

@@ -117,6 +117,9 @@ module.exports = function (sequelize, DataTypes) {
return yield this.find({ where: { name: name } }); return yield this.find({ where: { name: name } });
}, },
listByNames: function* (names) { listByNames: function* (names) {
if (!names || names.length === 0) {
return [];
}
return yield this.findAll({ return yield this.findAll({
where: { where: {
name: { name: {
@@ -152,7 +155,7 @@ module.exports = function (sequelize, DataTypes) {
user.json = data; user.json = data;
user.email = data.email || ''; user.email = data.email || '';
user.rev = data._rev || ''; user.rev = data._rev || '';
if (user.isDirty) { if (user.changed()) {
user = yield user.save(); user = yield user.save();
} }
return user; return user;
@@ -179,7 +182,7 @@ module.exports = function (sequelize, DataTypes) {
user.rev = rev; user.rev = rev;
user.salt = salt; user.salt = salt;
user.password_sha = passwordSha; user.password_sha = passwordSha;
if (user.isDirty) { if (user.changed()) {
user = yield user.save(); user = yield user.save();
} }
return user; return user;

View File

@@ -1,10 +1,11 @@
{ {
"name": "cnpmjs.org", "name": "cnpmjs.org",
"version": "2.0.0-rc.13", "version": "2.13.0",
"description": "Private npm registry and web for Enterprise, base on MySQL and Simple Store Service", "description": "Private npm registry and web for Enterprise, base on MySQL and Simple Store Service",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "make install && make jshint && make test", "dev": "DEBUG=cnpm* node dispatch.js",
"test": "make jshint && make test",
"start": "./bin/nodejsctl start && cp History.md docs/web/history.md", "start": "./bin/nodejsctl start && cp History.md docs/web/history.md",
"status": "./bin/nodejsctl status", "status": "./bin/nodejsctl status",
"stop": "./bin/nodejsctl stop" "stop": "./bin/nodejsctl stop"
@@ -13,60 +14,69 @@
"cnpmjs.org": "bin/cli.js" "cnpmjs.org": "bin/cli.js"
}, },
"dependencies": { "dependencies": {
"agentkeepalive": "~1.2.0", "agentkeepalive": "^2.2.0",
"bluebird": "~2.9.6", "await-event": "^1.0.0",
"bytes": "~1.0.0", "bytes": "^2.4.0",
"cfork": "~1.2.2", "cfork": "^1.5.1",
"co": "~3.1.0", "changes-stream": "^1.1.0",
"co-defer": "~0.1.2", "co": "^4.6.0",
"co-gather": "~0.0.1", "co-defer": "^1.0.0",
"co-sleep": "~0.0.1", "co-gather": "^0.0.1",
"commander": "~2.6.0", "co-sleep": "^0.0.1",
"copy-to": "~2.0.1", "commander": "^2.9.0",
"debug": "~2.1.1", "copy-to": "^2.0.1",
"error-formater": "~1.0.3", "debug": "^2.2.0",
"fs-cnpm": "~1.2.0", "error-formater": "^1.0.3",
"giturl": "~0.0.3", "fs-cnpm": "^1.2.0",
"gnode": "~0.1.1", "giturl": "^1.0.0",
"graceful": "~1.0.0", "graceful": "^1.0.0",
"gravatar": "~1.1.0", "gravatar": "^1.5.0",
"humanize-ms": "~1.0.1", "humanize-ms": "^1.2.0",
"humanize-number": "~0.0.2", "humanize-number": "^0.0.2",
"koa": "~0.13.0", "is-type-of": "^1.0.0",
"koa-limit": "~1.0.2", "kcors": "^1.2.1",
"koa-markdown": "~2.0.1", "koa": "^1.2.0",
"koa-middlewares": "~2.1.0", "koa-limit": "^1.0.2",
"koa-mock": "~1.1.4", "koa-markdown": "^2.0.1",
"markdown-it": "~3.0.4", "koa-maxrequests": "^1.0.0",
"mime": "~1.2.11", "koa-middlewares": "^2.1.0",
"mini-logger": "~1.0.0", "koa-mock": "^1.6.1",
"mkdirp": "~0.5.0", "koa-safe-jsonp": "^0.3.1",
"moment": "~2.9.0", "markdown-it": "^3.0.6",
"mysql": "~2.5.4", "mime": "^1.3.4",
"nodemailer": "~1.3.0", "mini-logger": "^1.1.1",
"semver": "~4.2.0", "mkdirp": "^0.5.0",
"sequelize": "~2.0.0-rc8", "moment": "^2.12.0",
"thunkify-wrap": "~1.0.4", "mysql": "^2.10.2",
"treekill": "~1.0.0", "mz": "^2.4.0",
"urllib": "~2.2.2", "nodemailer": "^1.3.0",
"utility": "~1.3.0", "semver": "^5.2.0",
"xss": "~0.1.20" "sequelize": "^3.23.4",
"thunkify-wrap": "^1.0.4",
"treekill": "^1.0.0",
"tunnel-agent": "^0.4.0",
"urllib": "^2.11.0",
"utility": "^1.8.0",
"xss": "^0.2.13"
}, },
"devDependencies": { "devDependencies": {
"autod": "*", "autod": "*",
"chunkstream": "*", "chunkstream": "*",
"co-mocha": "*",
"contributors": "*", "contributors": "*",
"istanbul-harmony": "*", "istanbul": "*",
"jshint": "*", "jshint": "*",
"mm": "*", "mm": "*",
"mocha": "*", "mocha": "*",
"node-dev": "*", "node-dev": "*",
"pedding": "*", "pedding": "*",
"should": "~4.0.4", "pg": "5",
"pg-hstore": "2",
"should": "8",
"should-http": "*", "should-http": "*",
"sqlite3": "*", "sqlite3": "*",
"supertest": "*" "supertest": "1",
"supertest-as-promised": "3",
"thunk-mocha": "1"
}, },
"homepage": "https://github.com/cnpm/cnpmjs.org", "homepage": "https://github.com/cnpm/cnpmjs.org",
"repository": { "repository": {
@@ -86,10 +96,10 @@
"registry" "registry"
], ],
"engines": { "engines": {
"node": ">= 0.11.14" "node": ">= 4.3.1"
}, },
"author": [ "author": [
"fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)", "fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)",
"dead_horse <dead_horse@qq.com> (http://deadhorse.me)" "dead_horse <dead_horse@qq.com> (http://deadhorse.me)"
], ],
"license": "MIT" "license": "MIT"

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

@@ -453,6 +453,7 @@
font-size: 85%; font-size: 85%;
line-height: 1.45; line-height: 1.45;
background-color: #f7f7f7; background-color: #f7f7f7;
color:#333;
border-radius: 3px; border-radius: 3px;
} }

View File

@@ -1,32 +1,144 @@
body { body {
font-size: 16px; font-size: 16px;
color: #333333;
} }
header {
max-width: 1140px; .mb15 {
min-width: 960px; margin-bottom: 15px !important;
margin: 30px auto 30px auto;
} }
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; display: inline-block;
} }
header #search-input {
#header .logo a {
display: inline-block;
vertical-align: top;
}
#search-input {
display: inline-block; display: inline-block;
width: 400px; width: 400px;
height: 50px; height: 50px;
font-size: 30px; font-size: 20px;
float: right; float: right;
margin-top: 22px; margin-top: 22px;
border-radius: 8px;
padding: 10px 20px;
transition: width .3s ease-in-out;
} }
@media (max-width: 1024px) {
#search-input {
width: 200px;
}
.pack-overview-background {
min-width: 960px;
}
}
#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 { .pack-overview {
min-width: 960px;
color: #f1f1f1; color: #f1f1f1;
height: 300px; height: 300px;
width: 100%; width: 100%;
margin-bottom: 50px;
position: relative;
} }
.pack-overview-background { .pack-overview-background {
height: 300px; height: 300px;
background: #428bca url(http://rockdai.u.qiniudn.com/bs-docs-masthead-pattern.png) repeat center center; background: #428bca url(https://dn-cnpm.qbox.me/bs-docs-masthead-pattern.png) repeat center center;
position: absolute; position: absolute;
width: 100%; width: 100%;
z-index: -1; z-index: -1;
@@ -34,13 +146,18 @@ header #search-input {
} }
.pack-overview a { .pack-overview a {
color: #f1f1f1; 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 { .pack-overview a:hover {
color: #ffffff; color: #ffffff;
opacity: .7;
} }
.pack-info { .pack-info {
position: relative;
max-width: 1140px; max-width: 1140px;
margin: 0 auto; margin: 0 auto;
padding: 30px 0; padding: 30px 0;
@@ -53,133 +170,134 @@ header #search-input {
line-height: 48px; line-height: 48px;
color: #fff; color: #fff;
} }
.pack-description { .pack-description {
line-height: 28px; font-family: "-apple-system", sans-serif;
text-overflow: ellipsis; margin-top: 20px;
color: #fff; display: block; /* Fallback for non-webkit */
display: -webkit-box;
width: 600px; width: 600px;
white-space:nowrap; font-size: 20px;
line-height: 26px;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow:hidden; max-height: 100px;
} }
.deprecated { .deprecated {
text-decoration: line-through; text-decoration: line-through;
color: #BDBDBD; color: #BDBDBD;
} }
.deprecated-content { .deprecated-content {
color: #FF8A65; color: #555;
width: 600px; width: 600px;
white-space:nowrap; white-space:nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow:hidden; overflow:hidden;
font-style: italic;
} }
.pack-ver { .pack-ver {
font-size: 28px; font-size: 28px;
line-height: 48px; line-height: 48px;
} }
.pack-repo {
margin-top: 50px; .pack-ver a.badge-link {
font-size: inherit;
border-bottom: 0px;
} }
.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 { .pack-install {
position: absolute; position: absolute;
top: 85px; top: 75px;
right: 0px; 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 { .pack-details a {
color: #333333; color: #333333;
} }
.cols-box {
display: flex; .pack-details a:hover {
-webkit-box-pack: center; border-bottom: 1px solid;
opacity: .8;
} }
.cols-box ul { .cols-box ul {
list-style: none; list-style: none;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
.cols-box-item { .cols-box-item {
flex: 1; margin-bottom: 15px;
height: 250px;
margin-right: 30px;
} }
.cols-box-item:last-child { .cols-box-item:last-child {
margin-right: 0; margin-right: 0;
} }
.cols-box-title { .cols-box-title {
font-size: 20px; font-size: 20px;
color: #428bca; color: #428bca;
margin-bottom: 10px; margin-bottom: 10px;
font-weight: bold; font-weight: bold;
overflow: hidden; overflow: hidden;
border-bottom: 1px solid #ccc;
padding-bottom: 3px;
} }
.cols-box-item .more-item { .cols-box-item .more-item {
font-size: 16px; font-size: 16px;
line-height: 28px; line-height: 28px;
color: #428bca; color: #428bca;
text-decoration: underline; margin-top: 15px;
float: right;
} }
.pack-dep-ver, .pack-download-num { .pack-dep-ver, .pack-download-num {
float: right; float: right;
} }
.pack-lastup, .pack-sync { .pack-lastup, .pack-sync {
margin-top: 10px; float: left;
}
.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;
} }
.maintainer { .maintainer {
-moz-box-flex: 1;
-webkit-box-flex: 1;
box-flex: 1;
margin-bottom: 10px; margin-bottom: 10px;
width: 50%; line-height: 30px;
-webkit-box-orient: vertical;
-webkit-box-pack: center;
line-height: 50px;
overflow: hidden; overflow: hidden;
} }
.maintainer a:hover {
font-style: italic;
border: none;
}
.maintainer .avatar { .maintainer .avatar {
-webkit-border-radius: 50px; -webkit-border-radius: 50px;
-moz-border-radius: 50px; -moz-border-radius: 50px;
border-radius: 50px; border-radius: 30px;
width: 30px;
height: 30px;
margin-right: 10px; margin-right: 10px;
} }
.downloads .row { ul, ol{
margin-left: 0; list-style: initial;
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-headline", "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

@@ -41,7 +41,7 @@ $(function () {
data.last_sync_module + '</a>'); data.last_sync_module + '</a>');
if (!data.sync_status) { if (!data.sync_status) {
$('.syncing').html(''); $('.syncing').remove();
} }
$('.sync').show(); $('.sync').show();

View File

@@ -1,12 +1,10 @@
/**! /**!
* cnpmjs.org - routes/registry.js
*
* Copyright(c) cnpmjs.org and other contributors. * Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
* dead_horse <dead_horse@qq.com> * dead_horse <dead_horse@qq.com>
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/ */
"use strict"; "use strict";
@@ -21,6 +19,7 @@ var publishable = require('../middleware/publishable');
var syncByInstall = require('../middleware/sync_by_install'); var syncByInstall = require('../middleware/sync_by_install');
var editable = require('../middleware/editable'); var editable = require('../middleware/editable');
var existsPackage = require('../middleware/exists_package'); var existsPackage = require('../middleware/exists_package');
var unpublishable = require('../middleware/unpublishable');
var showTotal = require('../controllers/total'); var showTotal = require('../controllers/total');
@@ -28,6 +27,7 @@ var listAll = require('../controllers/registry/package/list_all');
var listShorts = require('../controllers/registry/package/list_shorts'); var listShorts = require('../controllers/registry/package/list_shorts');
var listSince = require('../controllers/registry/package/list_since'); var listSince = require('../controllers/registry/package/list_since');
var listAllVersions = require('../controllers/registry/package/list'); var listAllVersions = require('../controllers/registry/package/list');
var listDependents = require('../controllers/registry/package/list_dependents');
var getOneVersion = require('../controllers/registry/package/show'); var getOneVersion = require('../controllers/registry/package/show');
var savePackage = require('../controllers/registry/package/save'); var savePackage = require('../controllers/registry/package/save');
var tag = require('../controllers/registry/package/tag'); var tag = require('../controllers/registry/package/tag');
@@ -36,6 +36,7 @@ var removeOneVersion = require('../controllers/registry/package/remove_version')
var updatePackage = require('../controllers/registry/package/update'); var updatePackage = require('../controllers/registry/package/update');
var downloadPackage = require('../controllers/registry/package/download'); var downloadPackage = require('../controllers/registry/package/download');
var downloadTotal = require('../controllers/registry/package/download_total'); 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 addUser = require('../controllers/registry/user/add');
var showUser = require('../controllers/registry/user/show'); var showUser = require('../controllers/registry/user/show');
@@ -65,9 +66,9 @@ function routes(app) {
// module // module
// scope package: params: [$name] // scope package: params: [$name]
app.get(/^\/(@[\w\-\.]+\/[\w\-\.]+)$/, syncByInstall, listAllVersions); app.get(/^\/(@[\w\-\.]+\/[^\/]+)$/, syncByInstall, listAllVersions);
// scope package: params: [$name, $version] // scope package: params: [$name, $version]
app.get(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/([\w\.\-]+)$/, syncByInstall, getOneVersion); app.get(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/([^\/]+)$/, syncByInstall, getOneVersion);
app.get('/:name', syncByInstall, listAllVersions); app.get('/:name', syncByInstall, listAllVersions);
app.get('/:name/:version', syncByInstall, getOneVersion); app.get('/:name/:version', syncByInstall, getOneVersion);
@@ -77,7 +78,9 @@ function routes(app) {
app.put('/:name', login, publishable, savePackage); app.put('/:name', login, publishable, savePackage);
// sync from source npm // sync from source npm
app.put(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/sync$/, sync.sync);
app.put('/:name/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); app.get('/:name/sync/log/:id', sync.getSyncLog);
// add tag // add tag
@@ -92,16 +95,16 @@ function routes(app) {
// delete tarball and remove one version // delete tarball and remove one version
app.delete(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/download\/(@[\w\-\.]+\/[\w\-\.]+)\/\-rev\/([\w\-\.]+)$/, app.delete(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/download\/(@[\w\-\.]+\/[\w\-\.]+)\/\-rev\/([\w\-\.]+)$/,
login, publishable, editable, removeOneVersion); login, unpublishable, removeOneVersion);
app.delete('/:name/download/:filename/-rev/:rev', login, publishable, editable, removeOneVersion); app.delete('/:name/download/:filename/-rev/:rev', login, unpublishable, removeOneVersion);
// update module, unpublish will PUT this // update module, unpublish will PUT this
app.put(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/\-rev\/([\w\-\.]+)$/, login, publishable, editable, updatePackage); app.put(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/\-rev\/([\w\-\.]+)$/, login, publishable, editable, updatePackage);
app.put('/:name/-rev/:rev', login, publishable, editable, updatePackage); app.put('/:name/-rev/:rev', login, publishable, editable, updatePackage);
// remove all versions // remove all versions
app.delete(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/\-rev\/([\w\-\.]+)$/, login, publishable, editable, removePackage); app.delete(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/\-rev\/([\w\-\.]+)$/, login, unpublishable, removePackage);
app.delete('/:name/-rev/:rev', login, publishable, editable, removePackage); app.delete('/:name/-rev/:rev', login, unpublishable, removePackage);
// try to create a new user // try to create a new user
// https://registry.npmjs.org/-/user/org.couchdb.user:fengmk2 // https://registry.npmjs.org/-/user/org.couchdb.user:fengmk2
@@ -111,11 +114,16 @@ function routes(app) {
// list all packages of user // list all packages of user
app.get('/-/by-user/:user', userPackage.list); app.get('/-/by-user/:user', userPackage.list);
app.get('/-/users/:user/packages', listPackagesByUser);
// download times // download times
app.get('/downloads/range/:range/:name', downloadTotal); app.get('/downloads/range/:range/:name', downloadTotal);
app.get('/downloads/range/:range', downloadTotal); app.get('/downloads/range/:range', downloadTotal);
// GET /-/package/:pkg/dependents
app.get('/-/package/:name/dependents', existsPackage, listDependents);
app.get(/^\/\-\/package\/(@[\w\-\.]+\/[\w\-\.]+)\/dependents$/, existsPackage, listDependents);
// GET /-/package/:pkg/dist-tags -- returns the package's dist-tags // GET /-/package/:pkg/dist-tags -- returns the package's dist-tags
app.get('/-/package/:name/dist-tags', existsPackage, tags.index); app.get('/-/package/:name/dist-tags', existsPackage, tags.index);
app.get(/^\/\-\/package\/(@[\w\-\.]+\/[\w\-\.]+)\/dist\-tags$/, existsPackage, tags.index); app.get(/^\/\-\/package\/(@[\w\-\.]+\/[\w\-\.]+)\/dist\-tags$/, existsPackage, tags.index);

View File

@@ -6,7 +6,7 @@
* *
* Authors: * Authors:
* dead_horse <dead_horse@qq.com> * dead_horse <dead_horse@qq.com>
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <m@fengmk2.com> (http://fengmk2.com)
*/ */
"use strict"; "use strict";
@@ -42,15 +42,19 @@ function routes(app) {
app.get('/~:name', showUser); app.get('/~:name', showUser);
app.get(/\/sync\/(@[\w\-\.]+\/[\w\-\.]+)$/, showSync);
app.get('/sync/:name', showSync); app.get('/sync/:name', showSync);
app.get('/sync', showSync); app.get('/sync', showSync);
app.put(/\/sync\/(@[\w\-\.]+\/[\w\-\.]+)$/, sync.sync);
app.put('/sync/:name', 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('/sync/:name/log/:id', sync.getSyncLog);
app.get('/_list/search/search', searchRange); app.get('/_list/search/search', searchRange);
app.get(/^\/badge\/v\/([@\w\-\.\/]+)\.svg$/, badge.version); app.get(/^\/badge\/v\/([@\w\-\.\/]+)\.svg$/, badge.version);
app.get(/^\/badge\/d\/([@\w\-\.\/]+)\.svg$/, badge.downloads);
} }
module.exports = routes; module.exports = routes;

View File

@@ -1,19 +1,4 @@
/**! 'use strict';
* cnpmjs.org - servers/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)
*/
"use strict";
/**
* Module dependencies.
*/
var koa = require('koa'); var koa = require('koa');
var app = module.exports = koa(); var app = module.exports = koa();
@@ -26,27 +11,29 @@ var block = require('../middleware/block');
var auth = require('../middleware/auth'); var auth = require('../middleware/auth');
var staticCache = require('../middleware/static'); var staticCache = require('../middleware/static');
var notFound = require('../middleware/registry_not_found'); var notFound = require('../middleware/registry_not_found');
var cors = require('../middleware/cors'); var cors = require('kcors');
var proxyToNpm = require('../middleware/proxy_to_npm'); var proxyToNpm = require('../middleware/proxy_to_npm');
var maxrequests = require('koa-maxrequests');
app.use(maxrequests());
app.use(block()); app.use(block());
middlewares.jsonp(app); middlewares.jsonp(app);
app.use(middlewares.rt({headerName: 'X-ReadTime'})); app.use(middlewares.rt({ headerName: 'X-ReadTime' }));
app.use(middlewares.rewrite('/favicon.ico', '/favicon.png')); app.use(middlewares.rewrite('/favicon.ico', '/favicon.png'));
app.use(staticCache); app.use(staticCache);
app.keys = ['todokey', config.sessionSecret]; app.keys = ['todokey', config.sessionSecret];
app.proxy = true; app.proxy = true;
app.use(proxyToNpm()); app.use(middlewares.bodyParser({ jsonLimit: config.jsonLimit }));
app.use(middlewares.bodyParser({jsonLimit: config.jsonLimit}));
app.use(cors({ app.use(cors({
methods: 'GET,HEAD' allowMethods: 'GET,HEAD',
})); }));
app.use(auth()); app.use(auth());
app.use(proxyToNpm());
app.use(notFound); app.use(notFound);
if (config.enableCompress) { if (config.enableCompress) {
app.use(middlewares.compress({threshold: 150})); app.use(middlewares.compress({ threshold: 150 }));
} }
app.use(middlewares.conditional()); app.use(middlewares.conditional());
app.use(middlewares.etag()); app.use(middlewares.etag());

View File

@@ -1,19 +1,4 @@
/**! 'use strict';
* cnpmjs.org - servers/web.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com>
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
"use strict";
/**
* Module dependencies.
*/
var opensearch = require('../middleware/opensearch'); var opensearch = require('../middleware/opensearch');
var notFound = require('../middleware/web_not_found'); var notFound = require('../middleware/web_not_found');
@@ -27,15 +12,20 @@ var auth = require('../middleware/auth');
var proxyToNpm = require('../middleware/proxy_to_npm'); var proxyToNpm = require('../middleware/proxy_to_npm');
var routes = require('../routes/web'); var routes = require('../routes/web');
var config = require('../config'); var config = require('../config');
var jsonp = require('koa-safe-jsonp');
var path = require('path'); var path = require('path');
var http = require('http'); var http = require('http');
var koa = require('koa'); var koa = require('koa');
var fs = require('fs'); var fs = require('fs');
var maxrequests = require('koa-maxrequests');
var app = koa(); var app = koa();
jsonp(app);
var rootdir = path.dirname(__dirname); var rootdir = path.dirname(__dirname);
app.use(maxrequests());
app.use(block()); app.use(block());
app.use(middlewares.rt({headerName: 'X-ReadTime'})); app.use(middlewares.rt({headerName: 'X-ReadTime'}));
app.use(middlewares.rewrite('/favicon.ico', '/favicon.png')); app.use(middlewares.rewrite('/favicon.ico', '/favicon.png'));
@@ -71,7 +61,8 @@ var layoutFile = path.join(viewDir, '_layout.html');
var footer = config.customFooter || fs.readFileSync(path.join(viewDir, 'footer.html'), 'utf8'); var footer = config.customFooter || fs.readFileSync(path.join(viewDir, 'footer.html'), 'utf8');
var layout = fs.readFileSync(path.join(viewDir, 'layout.html'), 'utf8') var layout = fs.readFileSync(path.join(viewDir, 'layout.html'), 'utf8')
.replace('{{footer}}', footer) .replace('{{footer}}', footer)
.replace('{{logoURL}}', config.logoURL); .replace('{{logoURL}}', config.logoURL)
.replace('{{adBanner}}', config.adBanner || '');
fs.writeFileSync(layoutFile, layout); fs.writeFileSync(layoutFile, layout);
// custom web readme home page support // custom web readme home page support

View File

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

View File

@@ -1,11 +1,9 @@
/**! /**!
* cnpmjs.org - services/download_total.js * Copyright(c) cnpm and other contributors.
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -14,6 +12,7 @@
* Module dependencies. * Module dependencies.
*/ */
var utility = require('utility');
var DownloadTotal = require('../models').DownloadTotal; var DownloadTotal = require('../models').DownloadTotal;
exports.getModuleTotal = function* (name, start, end) { exports.getModuleTotal = function* (name, start, end) {
@@ -31,6 +30,27 @@ exports.getModuleTotal = function* (name, start, end) {
return formatRows(rows, 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) { exports.plusModuleTotal = function* (data) {
var yearMonth = parseYearMonth(data.date); var yearMonth = parseYearMonth(data.date);
// all module download total // all module download total
@@ -47,8 +67,12 @@ exports.plusModuleTotal = function* (data) {
}); });
} }
var field = 'd' + data.date.substring(8, 10); var field = 'd' + data.date.substring(8, 10);
if (typeof row[field] === 'string') {
// pg bigint is string...
row[field] = utility.toSafeNumber(row[field]);
}
row[field] += data.count; row[field] += data.count;
if (row.isDirty) { if (row.changed()) {
yield row.save(); yield row.save();
} }
@@ -65,15 +89,19 @@ exports.plusModuleTotal = function* (data) {
}); });
} }
var field = 'd' + data.date.substring(8, 10); var field = 'd' + data.date.substring(8, 10);
if (typeof row[field] === 'string') {
// pg bigint is string...
row[field] = utility.toSafeNumber(row[field]);
}
row[field] += data.count; row[field] += data.count;
if (row.isDirty) { if (row.changed()) {
return yield row.save(); return yield row.save();
} }
return row; return row;
}; };
exports.getTotal = function* (start, end) { exports.getTotal = function* (start, end) {
return yield* exports.getModuleTotal('__all__', start, end); return yield exports.getModuleTotal('__all__', start, end);
}; };
function parseYearMonth(date) { function parseYearMonth(date) {
@@ -92,6 +120,9 @@ function formatRows(rows, startDate, endDate) {
var field = 'd' + day; var field = 'd' + day;
var d = yearMonth + '-' + day; var d = yearMonth + '-' + day;
var count = row[field]; var count = row[field];
if (typeof count === 'string') {
count = utility.toSafeNumber(count);
}
if (count > 0 && d >= startDate && d <= endDate) { if (count > 0 && d >= startDate && d <= endDate) {
dates.push({ dates.push({
name: row.name, name: row.name,

View File

@@ -50,5 +50,5 @@ exports.append = function* (id, log) {
}; };
exports.get = function* (id) { exports.get = function* (id) {
return yield ModuleLog.find(Number(id)); return yield ModuleLog.findById(id);
}; };

View File

@@ -15,6 +15,7 @@
* Module dependencies. * Module dependencies.
*/ */
var ms = require('humanize-ms');
var urllib = require('../common/urllib'); var urllib = require('../common/urllib');
var config = require('../config'); var config = require('../config');
@@ -28,6 +29,7 @@ function* request(url, options) {
'user-agent': USER_AGENT 'user-agent': USER_AGENT
}; };
options.gzip = true; options.gzip = true;
options.followRedirect = true;
var registry = options.registry || config.sourceNpmRegistry; var registry = options.registry || config.sourceNpmRegistry;
url = registry + url; url = registry + url;
var r; var r;
@@ -38,7 +40,8 @@ function* request(url, options) {
var data = err.data || '[empty]'; var data = err.data || '[empty]';
if (err.name === 'JSONResponseFormatError' && statusCode >= 500) { if (err.name === 'JSONResponseFormatError' && statusCode >= 500) {
err.name = 'NPMServerError'; err.name = 'NPMServerError';
err.message = 'Status ' + statusCode + ', ' + data.toString(); err.status = statusCode;
err.message = 'Url: ' + url + ', Status ' + statusCode + ', ' + data.toString();
} }
throw err; throw err;
} }
@@ -67,6 +70,83 @@ exports.get = function* (name) {
return data; 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) { exports.getAllSince = function* (startkey, timeout) {
var r = yield* request('/-/all/since?stale=update_after&startkey=' + startkey, { var r = yield* request('/-/all/since?stale=update_after&startkey=' + startkey, {
timeout: timeout || 300000 timeout: timeout || 300000
@@ -74,6 +154,14 @@ exports.getAllSince = function* (startkey, timeout) {
return r.data; 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) { exports.getShort = function* (timeout) {
var r = yield* request('/-/short', { var r = yield* request('/-/short', {
timeout: timeout || 300000, timeout: timeout || 300000,

View File

@@ -1,11 +1,9 @@
/**! /**
* cnpmjs.org - services/package.js * Copyright(c) cnpm and other contributors.
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -14,6 +12,7 @@
* Module dependencies. * Module dependencies.
*/ */
var semver = require('semver');
var models = require('../models'); var models = require('../models');
var common = require('./common'); var common = require('./common');
var Tag = models.Tag; var Tag = models.Tag;
@@ -27,16 +26,23 @@ var ModuleUnpublished = models.ModuleUnpublished;
var NpmModuleMaintainer = models.NpmModuleMaintainer; var NpmModuleMaintainer = models.NpmModuleMaintainer;
// module // 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 // module:read
function parseRow(row) { function parseRow(row) {
if (row && row.package) { if (row && row.package) {
try { try {
if (row.package.indexOf('%7B%22') === 0) { _parseRow(row);
// now store package will encodeURIComponent() after JSON.stringify
row.package = decodeURIComponent(row.package);
}
row.package = JSON.parse(row.package);
} catch (e) { } catch (e) {
console.warn('parse package error: %s, id: %s version: %s, error: %s', row.name, row.id, row.version, e); console.warn('parse package error: %s, id: %s version: %s, error: %s', row.name, row.id, row.version, e);
} }
@@ -49,7 +55,7 @@ function stringifyPackage(pkg) {
} }
exports.getModuleById = function* (id) { exports.getModuleById = function* (id) {
var row = yield Module.find(Number(id)); var row = yield Module.findById(Number(id));
parseRow(row); parseRow(row);
return row; return row;
}; };
@@ -68,6 +74,25 @@ exports.getModuleByTag = function* (name, tag) {
return yield* exports.getModule(tag.name, tag.version); return yield* exports.getModule(tag.name, tag.version);
}; };
exports.getModuleByRange = function* (name, range) {
var rows = yield exports.listModulesByName(name, [ 'id', 'version' ]);
var versionMap = {};
var versions = rows.map(function(row) {
versionMap[row.version] = row;
return row.version;
}).filter(function(version) {
return semver.valid(version);
});
var version = semver.maxSatisfying(versions, range);
if (!versionMap[version]) {
return null;
}
var id = versionMap[version].id;
return yield exports.getModuleById(id);
};
exports.getLatestModule = function* (name) { exports.getLatestModule = function* (name) {
return yield* exports.getModuleByTag(name, 'latest'); return yield* exports.getModuleByTag(name, 'latest');
}; };
@@ -124,7 +149,7 @@ exports.listModules = function* (names) {
id: ids id: ids
}, },
attributes: [ attributes: [
'name', 'description' 'name', 'description', 'version',
] ]
}); });
return rows; return rows;
@@ -215,23 +240,25 @@ exports.listAllPublicModuleNames = function* () {
var sql = 'SELECT DISTINCT(name) AS name FROM tag ORDER BY name'; var sql = 'SELECT DISTINCT(name) AS name FROM tag ORDER BY name';
var rows = yield models.query(sql); var rows = yield models.query(sql);
return rows.filter(function (row) { return rows.filter(function (row) {
return row.name[0] !== '@'; return !common.isPrivatePackage(row.name);
}).map(function (row) { }).map(function (row) {
return row.name; return row.name;
}); });
}; };
exports.listModulesByName = function* (moduleName) { exports.listModulesByName = function* (moduleName, attributes) {
var mods = yield Module.findAll({ var mods = yield Module.findAll({
where: { where: {
name: moduleName name: moduleName
}, },
order: [ ['id', 'DESC'] ] order: [ ['id', 'DESC'] ],
attributes,
}); });
return mods.map(function (mod) {
for (var mod of mods) {
parseRow(mod); parseRow(mod);
return mod; }
}); return mods;
}; };
exports.getModuleLastModified = function* (name) { exports.getModuleLastModified = function* (name) {
@@ -276,7 +303,7 @@ exports.saveModule = function* (mod) {
item.dist_size = dist.size; item.dist_size = dist.size;
item.description = description; item.description = description;
if (item.isDirty) { if (item.changed()) {
item = yield item.save(); item = yield item.save();
} }
var result = { var result = {
@@ -308,7 +335,7 @@ exports.saveModule = function* (mod) {
}; };
exports.updateModulePackage = function* (id, pkg) { exports.updateModulePackage = function* (id, pkg) {
var mod = yield Module.find(Number(id)); var mod = yield Module.findById(Number(id));
if (!mod) { if (!mod) {
// not exists // not exists
return null; return null;
@@ -361,8 +388,9 @@ exports.updateModuleLastModified = function* (name) {
if (!row) { if (!row) {
return null; return null;
} }
row.gmt_modified = new Date(); // gmt_modified is readonly, we must use setDataValue
return yield row.save(['gmt_modified']); row.setDataValue('gmt_modified', new Date());
return yield row.save();
}; };
exports.removeModulesByName = function* (name) { exports.removeModulesByName = function* (name) {
@@ -399,7 +427,7 @@ exports.addModuleTag = function* (name, tag, version) {
} }
row.module_id = mod.id; row.module_id = mod.id;
row.version = version; row.version = version;
if (row.isDirty) { if (row.changed()) {
return yield row.save(); return yield row.save();
} }
return row; return row;
@@ -496,8 +524,7 @@ exports.updatePrivateModuleMaintainers = function* (name, usernames) {
}; };
function* getMaintainerModel(name) { function* getMaintainerModel(name) {
var isPrivatePackage = yield* common.isPrivatePackage(name); return common.isPrivatePackage(name) ? PrivateModuleMaintainer : NpmModuleMaintainer;
return isPrivatePackage ? PrivateModuleMaintainer : NpmModuleMaintainer;
} }
exports.listMaintainers = function* (name) { exports.listMaintainers = function* (name) {
@@ -576,7 +603,7 @@ exports.addKeyword = function* (data) {
item = ModuleKeyword.build(data); item = ModuleKeyword.build(data);
} }
item.description = data.description; item.description = data.description;
if (item.isDirty) { if (item.changed()) {
// make sure object will change, otherwise will cause empty sql error // make sure object will change, otherwise will cause empty sql error
// @see https://github.com/cnpm/cnpmjs.org/issues/533 // @see https://github.com/cnpm/cnpmjs.org/issues/533
return yield item.save(); return yield item.save();
@@ -609,7 +636,7 @@ exports.search = function* (word, options) {
// 3. keyword equal search // 3. keyword equal search
var ids = {}; var ids = {};
var sql = 'SELECT module_id FROM tag WHERE LOWER(name) LIKE LOWER(?) AND tag="latest" \ var sql = 'SELECT module_id FROM tag WHERE LOWER(name) LIKE LOWER(?) AND tag=\'latest\' \
ORDER BY name LIMIT ?;'; ORDER BY name LIMIT ?;';
var rows = yield* models.query(sql, [word + '%', limit ]); var rows = yield* models.query(sql, [word + '%', limit ]);
for (var i = 0; i < rows.length; i++) { for (var i = 0; i < rows.length; i++) {

View File

@@ -1,11 +1,9 @@
/**! /**
* cnpmjs.org - services/total.js * Copyright(c) cnpm and other contributors.
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -18,15 +16,20 @@ var config = require('../config');
var models = require('../models'); var models = require('../models');
var Total = models.Total; var Total = models.Total;
var TOTAL_MODULE_SQL = 'SELECT count(distinct(name)) AS count FROM module;';
var TOTAL_VERSION_SQL = 'SELECT count(name) AS count FROM module;';
var TOTAL_USER_SQL = 'SELECT count(name) AS count FROM user;';
if (config.database.dialect === 'postgres') {
// pg not allow table name as 'user'
TOTAL_USER_SQL = 'SELECT count(name) AS count FROM public.user;';
}
exports.get = function* () { exports.get = function* () {
// var DB_SIZE_SQL = 'SELECT TABLE_NAME AS name, data_length, index_length \ // var DB_SIZE_SQL = 'SELECT TABLE_NAME AS name, data_length, index_length \
// FROM information_schema.tables WHERE TABLE_SCHEMA = ? \ // FROM information_schema.tables WHERE TABLE_SCHEMA = ? \
// GROUP BY TABLE_NAME \ // GROUP BY TABLE_NAME \
// ORDER BY data_length DESC \ // ORDER BY data_length DESC \
// LIMIT 0, 200'; // LIMIT 0, 200';
var TOTAL_MODULE_SQL = 'SELECT count(distinct(name)) AS count FROM `module`;';
var TOTAL_VERSION_SQL = 'SELECT count(name) AS count FROM `module`;';
var TOTAL_USER_SQL = 'SELECT count(name) AS count FROM `user`;';
var rs = yield [ var rs = yield [
// models.query(DB_SIZE_SQL, [config.db]), // models.query(DB_SIZE_SQL, [config.db]),
models.queryOne(TOTAL_MODULE_SQL), models.queryOne(TOTAL_MODULE_SQL),
@@ -41,6 +44,10 @@ exports.get = function* () {
var uc = rs[2]; var uc = rs[2];
var info = rs[3] || {}; var info = rs[3] || {};
if (typeof info.module_delete === 'string') {
info.module_delete = Number(info.module_delete);
}
var total = { var total = {
data_tables: {}, data_tables: {},
disk_size: 0, disk_size: 0,
@@ -82,42 +89,49 @@ exports.get = function* () {
}; };
exports.getTotalInfo = function* () { exports.getTotalInfo = function* () {
return yield Total.find({ var row = yield Total.find({
where: { where: {
name: 'total' name: 'total'
} }
}); });
if (row && typeof row.module_delete === 'string') {
row.module_delete = Number(row.module_delete);
}
return row;
}; };
exports.plusDeleteModule = function* () { exports.plusDeleteModule = function* () {
var sql = 'UPDATE total SET module_delete=module_delete+1 WHERE name="total"'; var sql = 'UPDATE total SET module_delete=module_delete+1 WHERE name=\'total\'';
return yield* models.query(sql); return yield* models.query(sql);
}; };
exports.setLastSyncTime = function* (time) { exports.setLastSyncTime = function* (time) {
var sql = 'UPDATE total SET last_sync_time=? WHERE name="total"'; var sql = 'UPDATE total SET last_sync_time=? WHERE name=\'total\'';
return yield* models.query(sql, [Number(time)]); return yield* models.query(sql, [Number(time)]);
}; };
exports.setLastExistSyncTime = function* (time) { exports.setLastExistSyncTime = function* (time) {
var sql = 'UPDATE total SET last_exist_sync_time=? WHERE name="total"'; var sql = 'UPDATE total SET last_exist_sync_time=? WHERE name=\'total\'';
return yield* models.query(sql, [Number(time)]); return yield* models.query(sql, [Number(time)]);
}; };
exports.updateSyncStatus = function* (status) { exports.updateSyncStatus = function* (status) {
var sql = 'UPDATE total SET sync_status=? WHERE name="total"'; var sql = 'UPDATE total SET sync_status=? WHERE name=\'total\'';
return yield* models.query(sql, [status]); return yield* models.query(sql, [status]);
}; };
exports.updateSyncNum = function* (params) { exports.updateSyncNum = function* (params) {
var arg = { var args = [
sync_status: params.syncStatus, params.syncStatus,
need_sync_num: params.need || 0, params.need || 0,
success_sync_num: params.success || 0, params.success || 0,
fail_sync_num: params.fail || 0, params.fail || 0,
left_sync_num: params.left || 0, params.left || 0,
last_sync_module: params.lastSyncModule params.lastSyncModule
}; ];
var sql = 'UPDATE total SET ? WHERE name="total"'; var sql = 'UPDATE total SET \
return yield* models.query(sql, [arg]); sync_status=?, need_sync_num=?, success_sync_num=?, \
fail_sync_num=?, left_sync_num=?, last_sync_module=? \
WHERE name=\'total\'';
return yield* models.query(sql, args);
}; };

View File

@@ -0,0 +1,85 @@
'use strict';
const ChangesStream = require('changes-stream');
const path = require('path');
const fs = require('mz/fs');
const urllib = require('urllib');
const streamAwait = require('await-event');
const logger = require('../common/logger');
const config = require('../config');
const db = 'https://replicate.npmjs.com';
const lastSeqFile = path.join(config.dataDir, '.cnpmjs.org.last_seq.txt');
let _STREAM_ID = 0;
module.exports = function* sync() {
const since = yield getLastSequence();
const streamId = _STREAM_ID++;
let changesCount = 0;
logger.syncInfo('start changes stream#%d, since: %s', streamId, since);
const changes = new ChangesStream({
db,
since,
include_docs: false,
});
changes.await = streamAwait;
changes.on('data', change => {
changesCount++;
logger.syncInfo('stream#%d get change#%d: %j', streamId, changesCount, change);
syncPackage(change);
});
try {
yield changes.await('error');
} catch (err) {
// make sure changes steam is destroy
changes.destroy();
err.message += `, stream#${streamId}, changesCount#${changesCount}`;
throw err;
}
};
function syncPackage(change) {
const url = `${config.handleSyncRegistry}/${change.id}/sync`;
urllib.request(url, {
method: 'PUT',
dataType: 'json',
timeout: 10000,
}, (err, data) => {
if (err) {
logger.syncInfo('%s:%s PUT %s error: %s, retry after 5s',
change.seq, change.id, url, err);
logger.syncError(err);
syncPackage(change);
setTimeout(() => syncPackage(change), 5000);
} else {
saveLastSequence(change.seq);
logger.syncInfo('%s:%s sync request sent, log: %s/log/%s',
change.seq, change.id, url, data.logId);
}
});
}
function* getLastSequence() {
let lastSeq;
if (yield fs.exists(lastSeqFile)) {
lastSeq = yield fs.readFile(lastSeqFile, 'utf8');
lastSeq = Number(lastSeq);
}
if (!lastSeq) {
lastSeq = 2649694;
}
// const r = yield urllib.request(db, {
// dataType: 'json',
// timeout: 15000,
// });
// logger.syncInfo('get registry info: %j', r.data);
// if (lastSeq < r.data.update_seq) {
// lastSeq = r.data.update_seq;
// }
return lastSeq;
}
function saveLastSequence(seq) {
fs.writeFile(lastSeqFile, String(seq), () => {});
}

View File

@@ -1,37 +1,23 @@
/**!
* cnpmjs.org - sync/index.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
'use strict'; 'use strict';
/** const debug = require('debug')('cnpmjs.org:sync:index');
* Module dependencies. const co = require('co');
*/ const ms = require('humanize-ms');
const util = require('util');
const config = require('../config');
const mail = require('../common/mail');
const logger = require('../common/logger');
const totalService = require('../services/total');
var debug = require('debug')('cnpmjs.org:sync:index'); let sync = null;
var co = require('co');
var ms = require('humanize-ms');
var util = require('util');
var config = require('../config');
var mail = require('../common/mail');
var logger = require('../common/logger');
var totalService = require('../services/total');
var sync = null;
switch (config.syncModel) { switch (config.syncModel) {
case 'all': case 'all':
sync = require('./sync_all'); sync = require('./sync_all');
break; break;
case 'exist': case 'exist':
sync = require('./sync_exist'); sync = require('./sync_exist');
break; break;
} }
if (!sync && config.enableCluster) { if (!sync && config.enableCluster) {
@@ -39,104 +25,131 @@ if (!sync && config.enableCluster) {
process.exit(0); process.exit(0);
} }
console.log('[%s] [sync_worker:%s] syncing with %s mode', console.log('[%s] [sync_worker:%s] syncing with %s mode, changesStreamingSync: %s',
Date(), process.pid, config.syncModel); Date(), process.pid, config.syncModel, config.changesStreamingSync);
//set sync_status = 0 at first function onerror(err) {
logger.error(err);
}
// set sync_status = 0 at first
co(function* () { co(function* () {
yield* totalService.updateSyncStatus(0); yield totalService.updateSyncStatus(0);
})(function (err) { yield checkSyncStatus();
if (err) { }).catch(onerror);
logger.error(err);
}
});
var syncInterval = ms(config.syncInterval); let syncInterval = ms(config.syncInterval);
var minSyncInterval = ms('5m'); const minSyncInterval = ms('5m');
if (!syncInterval || syncInterval < minSyncInterval) { if (!syncInterval || syncInterval < minSyncInterval) {
syncInterval = minSyncInterval; syncInterval = minSyncInterval;
} }
// the same time only sync once
var syncing = false;
var handleSync = co(function* () {
debug('mode: %s, syncing: %s', config.syncModel, syncing);
if (!syncing) {
syncing = true;
debug('start syncing');
var data;
var error;
try {
data = yield* sync();
} catch (err) {
error = err;
error.message += ' (sync package error)';
logger.syncError(error);
}
data && logger.syncInfo(data);
if (!config.debug) {
sendMailToAdmin(error, data, new Date());
}
syncing = false;
}
});
if (sync) { if (sync) {
handleSync(); // the same time only sync once
setInterval(handleSync, syncInterval); let syncing = false;
const syncFn = co.wrap(function*() {
debug('mode: %s, syncing: %s', config.syncModel, syncing);
if (!syncing) {
syncing = true;
debug('start syncing');
let data;
let error;
try {
data = yield sync();
} catch (err) {
error = err;
error.message += ' (sync package error)';
logger.syncError(error);
}
data && logger.syncInfo(data);
if (!config.debug) {
sendMailToAdmin(error, data, new Date());
}
syncing = false;
}
// check last_sync_time and last_exist_sync_time
yield checkSyncStatus();
});
syncFn().catch(onerror);
setInterval(() => syncFn().catch(onerror), syncInterval);
} }
/** /**
* sync popular modules * sync popular modules
*/ */
var startSyncPopular = require('./sync_popular');
var syncingPopular = false;
var syncPopular = co(function* syncPopular() {
if (syncingPopular) {
return;
}
syncingPopular = true;
logger.syncInfo('Start syncing popular modules...');
var data;
var error;
try {
data = yield* startSyncPopular();
} catch (err) {
error = err;
error.message += ' (sync package error)';
logger.syncError(error);
}
if (data) {
logger.syncInfo(data);
}
if (!config.debug) {
sendMailToAdmin(error, data, new Date());
}
syncingPopular = false;
});
if (config.syncPopular) { if (config.syncPopular) {
syncPopular(); const sync = require('./sync_popular');
setInterval(syncPopular, ms(config.syncPopularInterval)); let syncing = false;
} else { const syncFn = co.wrap(function*() {
logger.syncInfo('sync popular module disable'); if (syncing) {
return;
}
syncing = true;
logger.syncInfo('Start syncing popular modules...');
let data;
let error;
try {
data = yield sync();
} catch (err) {
error = err;
error.message += ' (sync package error)';
logger.syncError(error);
}
if (data) {
logger.syncInfo(data);
}
if (!config.debug) {
sendMailToAdmin(error, data, new Date());
}
syncing = false;
});
syncFn().catch(onerror);
setInterval(() => syncFn().catch(onerror), ms(config.syncPopularInterval));
}
if (config.syncChangesStream) {
const sync = require('./changes_stream_syncer');
let syncing = false;
const syncFn = co.wrap(function*() {
if (syncing) {
return;
}
syncing = true;
logger.syncInfo('Start changes stream syncing...');
try {
yield sync();
} catch (err) {
err.message += ' (sync changes stream error)';
logger.syncError(err);
}
syncing = false;
});
syncFn().catch(onerror);
setInterval(() => syncFn().catch(onerror), ms('1m'));
} }
function sendMailToAdmin(err, result, syncTime) { function sendMailToAdmin(err, result, syncTime) {
result = result || {}; result = result || {};
var to = []; const to = [];
for (var name in config.admins) { for (var name in config.admins) {
to.push(config.admins[name]); to.push(config.admins[name]);
} }
debug('Send email to all admins: %j, with err message: %s, result: %j, start sync time: %s.', debug('Send email to all admins: %j, with err message: %s, result: %j, start sync time: %s.',
to, err ? err.message : '', result, syncTime); to, err ? err.message : '', result, syncTime);
var subject; let subject;
var type; let type;
var html; let html;
if (err) { if (err) {
// ignore 503 error
if (err.status === 503) {
return;
}
subject = 'Sync Error'; subject = 'Sync Error';
type = 'error'; type = 'error';
html = util.format('Sync packages from official registry failed.\n' + html = util.format('Sync packages from official registry failed.\n' +
@@ -158,10 +171,37 @@ function sendMailToAdmin(err, result, syncTime) {
debug('send email with type: %s, subject: %s, html: %s', type, subject, html); debug('send email with type: %s, subject: %s, html: %s', type, subject, html);
logger.syncInfo('send email with type: %s, subject: %s, html: %s', type, subject, html); logger.syncInfo('send email with type: %s, subject: %s, html: %s', type, subject, html);
if (type && type !== 'log') { if (type && type !== 'log') {
mail[type](to, subject, html, function (err) { mail[type](to, subject, html, err => {
if (err) { if (err) {
logger.error(err); logger.error(err);
} }
}); });
} }
} }
function* checkSyncStatus() {
// if source registry not cnpm, ignore it. e.g.: cnpmjs.org source registry is npmjs.org
if (!config.sourceNpmRegistryIsCNpm) {
return;
}
const total = yield totalService.getTotalInfo();
let lastSyncTime;
if (config.syncModel === 'all') {
lastSyncTime = total.last_sync_time;
} else if (config.syncModel === 'exist') {
lastSyncTime = total.last_exist_sync_time;
}
debug('checkSyncStatus start, lastSyncTime: %s, syncInterval: %s', lastSyncTime, syncInterval);
if (!lastSyncTime) {
return;
}
const diff = Date.now() - lastSyncTime;
const oneDay = 3600000 * 24;
const maxTime = Math.max(oneDay, syncInterval * 2);
if (diff > maxTime) {
const err = new Error('Last sync time is expired in ' + diff + ' ms, lastSyncTime: ' +
new Date(lastSyncTime) + ', maxTime: ' + maxTime + ' ms');
err.name = 'SyncExpiredError';
sendMailToAdmin(err, null, new Date());
}
}

View File

@@ -1,19 +1,5 @@
/*!
* cnpmjs.org - sync/status.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
'use strict'; 'use strict';
/**
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:sync:status'); var debug = require('debug')('cnpmjs.org:sync:status');
var co = require('co'); var co = require('co');
var Total = require('../services/total'); var Total = require('../services/total');
@@ -36,8 +22,8 @@ Status.prototype.log = function (syncDone) {
lastSyncModule: this.lastSyncModule, lastSyncModule: this.lastSyncModule,
}; };
co(function* () { co(function* () {
yield* Total.updateSyncNum(params); yield Total.updateSyncNum(params);
})(); }).catch(function () {});
}; };
Status.prototype.start = function () { Status.prototype.start = function () {

View File

@@ -1,20 +1,5 @@
/*!
* cnpmjs.org - sync/sync_all.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
'use strict'; 'use strict';
/**
* Module dependencies.
*/
var ms = require('humanize-ms');
var thunkify = require('thunkify-wrap'); var thunkify = require('thunkify-wrap');
var config = require('../config'); var config = require('../config');
var Status = require('./status'); var Status = require('./status');
@@ -40,19 +25,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() { module.exports = function* sync() {
var syncTime = Date.now(); var syncTime = Date.now();
var info = yield* totalService.getTotalInfo(); var info = yield* totalService.getTotalInfo();
@@ -66,7 +38,9 @@ module.exports = function* sync() {
logger.syncInfo('First time sync all packages from official registry'); logger.syncInfo('First time sync all packages from official registry');
packages = yield* getFirstSyncPackages(info.last_sync_module); packages = yield* getFirstSyncPackages(info.last_sync_module);
} else { } 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 || []; packages = packages || [];
@@ -83,19 +57,22 @@ module.exports = function* sync() {
concurrency: config.syncConcurrency, concurrency: config.syncConcurrency,
syncUpstreamFirst: false, syncUpstreamFirst: false,
}); });
Status.init({need: packages.length}, worker); Status.init({
need: packages.length,
}, worker);
worker.start(); worker.start();
var end = thunkify.event(worker); var end = thunkify.event(worker);
yield end(); yield end();
logger.syncInfo('All packages sync done, successes %d, fails %d', logger.syncInfo('All packages sync done, successes %d, fails %d, updates %d',
worker.successes.length, worker.fails.length); worker.successes.length, worker.fails.length, worker.updates.length);
//only when all succss, set last sync time //only when all succss, set last sync time
if (!worker.fails.length) { if (!worker.fails.length) {
yield* totalService.setLastSyncTime(syncTime); yield totalService.setLastSyncTime(syncTime);
} }
return { return {
successes: worker.successes, successes: worker.successes,
fails: worker.fails fails: worker.fails,
updates: worker.updates,
}; };
}; };

View File

@@ -1,22 +1,7 @@
/*!
* cnpmjs.org - sync/sync_exist.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
'use strict'; 'use strict';
/**
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:sync:sync_exist'); var debug = require('debug')('cnpmjs.org:sync:sync_exist');
var Status = require('./status'); var Status = require('./status');
var ms = require('humanize-ms');
var thunkify = require('thunkify-wrap'); var thunkify = require('thunkify-wrap');
var config = require('../config'); var config = require('../config');
var npmService = require('../services/npm'); var npmService = require('../services/npm');
@@ -34,7 +19,7 @@ function intersection(arrOne, arrTwo) {
map[name] = true; map[name] = true;
}); });
arrTwo.forEach(function (name) { arrTwo.forEach(function (name) {
map[name] && results.push(name); map[name] === true && results.push(name);
}); });
return results; return results;
} }
@@ -51,7 +36,7 @@ module.exports = function* sync() {
var allPackages; var allPackages;
if (!info.last_exist_sync_time) { if (!info.last_exist_sync_time) {
var pkgs = yield* npmService.getShort(); var pkgs = yield npmService.getShort();
debug('First time sync all packages from official registry, got %d packages', pkgs.length); debug('First time sync all packages from official registry, got %d packages', pkgs.length);
if (info.last_sync_module) { if (info.last_sync_module) {
// start from last success // start from last success
@@ -63,16 +48,10 @@ module.exports = function* sync() {
} }
allPackages = pkgs; allPackages = pkgs;
} else { } else {
debug('sync new module from last exist sync time: %s', info.last_sync_time); debug('sync new module from last exist sync time: %s', info.last_exist_sync_time);
var data = yield* npmService.getAllSince(info.last_exist_sync_time - ms('10m')); var result = yield npmService.fetchUpdatesSince(info.last_exist_sync_time);
if (!data) { allPackages = result.names;
allPackages = []; syncTime = result.lastModified;
}
if (data._updated) {
syncTime = data._updated;
delete data._updated;
}
allPackages = Object.keys(data);
} }
var packages = intersection(existPackages, allPackages); var packages = intersection(existPackages, allPackages);
@@ -83,7 +62,7 @@ module.exports = function* sync() {
fails: [] fails: []
}; };
} }
debug('Total %d packages to sync', packages.length); debug('Total %d packages to sync, top 10: %j', packages.length, packages.slice(0, 10));
var worker = new SyncModuleWorker({ var worker = new SyncModuleWorker({
username: 'admin', username: 'admin',

View File

@@ -1,19 +1,5 @@
/*!
* cnpmjs.org - sync/sync_popular.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
'use strict'; 'use strict';
/**
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:sync:sync_popular'); var debug = require('debug')('cnpmjs.org:sync:sync_popular');
var thunkify = require('thunkify-wrap'); var thunkify = require('thunkify-wrap');
var config = require('../config'); var config = require('../config');

52
sync/sync_since.js Normal file
View File

@@ -0,0 +1,52 @@
'use strict';
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

@@ -5,7 +5,7 @@
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <m@fengmk2.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -14,23 +14,30 @@
* Module dependencies. * Module dependencies.
*/ */
var path = require('path'); var utils = require('../utils');
var fs = require('fs');
var markdown = require('../../common/markdown'); var markdown = require('../../common/markdown');
describe('common/markdown.test.js', function () { describe('common/markdown.test.js', function () {
var fixtures = path.join(__dirname, '..', 'fixtures');
it('should render sonido readme', function () { it('should render sonido readme', function () {
var readme = fs.readFileSync(path.join(fixtures, 'sonido.md'), 'utf8'); var readme = utils.getFileContent('sonido.md');
var md = markdown.render(readme); var md = markdown.render(readme);
md.should.equal('<p>Configuration Wizard: &lt;!--- This is what the user will see during the configuration -&gt;</p>\n'); md.should.equal('<p>Configuration Wizard: &lt;!--- This is what the user will see during the configuration -&gt;</p>\n');
}); });
it('should filter xss', function () { it('should filter xss', function () {
var html = markdown.render('foo<script>alert(1)</script>/xss\n[foo](/foo) <a onclick="alert(1)">bar</a>\n"\''); var html = markdown.render('foo<script>alert(1)</script>/xss\n[foo](/foo) <a onclick="alert(1)">bar</a>\n"\'');
// console.log(html);
html.should.equal('<p>foo&lt;script&gt;alert(1)&lt;/script&gt;/xss\n<a href="/foo">foo</a> <a>bar</a>\n&quot;\'</p>\n'); html.should.equal('<p>foo&lt;script&gt;alert(1)&lt;/script&gt;/xss\n<a href="/foo">foo</a> <a>bar</a>\n&quot;\'</p>\n');
markdown.render('[xss link](javascript:alert(2))').should.equal('<p>[xss link](javascript:alert(2))</p>\n'); markdown.render('[xss link](javascript:alert(2))').should.equal('<p>[xss link](javascript:alert(2))</p>\n');
}); });
it('should handle eat cpu markdown', function () {
var readme = utils.getFileContent('eat-cpu.md');
markdown.render(readme);
});
it('should not filter < and > on code', function () {
var content = utils.getFileContent('code.md');
var html = markdown.render(content);
html.should.containEql('<pre><code class="language-html">&lt;body&gt;hi&lt;/body&gt;');
});
}); });

View File

@@ -1,13 +1,3 @@
/**!
* cnpmjs.org - test/controllers/registry/module/scope_package.test.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict'; 'use strict';
/** /**
@@ -21,7 +11,7 @@ var config = require('../../../../config');
var app = require('../../../../servers/registry'); var app = require('../../../../servers/registry');
var utils = require('../../../utils'); var utils = require('../../../utils');
describe('controllers/registry/module/scope_package.test.js', function () { describe('test/controllers/registry/module/scope_package.test.js', function () {
var pkgname = '@cnpm/test-scope-package'; var pkgname = '@cnpm/test-scope-package';
var pkgURL = '/@' + encodeURIComponent(pkgname.substring(1)); var pkgURL = '/@' + encodeURIComponent(pkgname.substring(1));
before(function (done) { before(function (done) {
@@ -52,16 +42,17 @@ describe('controllers/registry/module/scope_package.test.js', function () {
afterEach(mm.restore); afterEach(mm.restore);
it('should get 404 when do not support scope', function (done) { it('should get 302 when do not support scope', function (done) {
mm(config, 'scopes', []); mm(config, 'scopes', []);
request(app) request(app)
.get('/@invalid/test') .get('/@invalid/test')
.expect(404, done); .expect('Location', 'https://registry.npmjs.com/@invalid/test')
.expect(302, done);
}); });
it('should get 400 when scope not match', function (done) { it('should get 404 when scope is private', function (done) {
request(app) request(app)
.get('/@invalid/test') .get('/@cnpmtest/test')
.expect(404, done); .expect(404, done);
}); });

View File

@@ -170,6 +170,18 @@ describe('controllers/registry/package/dist_tag.test.js', function () {
}); });
}); });
it('should not destroy latest tag', function(done) {
request(app.listen())
.delete('/-/package/@cnpmtest/dist_tag_test_module_destroy/dist-tags/latest')
.set('authorization', utils.otherUserAuth)
.set('content-type', 'application/json')
.expect({
error: 'dist_tag_error',
reason: 'Can\'t not delete latest tag',
})
.expect(400, done);
});
it('should 404 destroy not exists tag', function (done) { it('should 404 destroy not exists tag', function (done) {
request(app.listen()) request(app.listen())
.delete('/-/package/@cnpmtest/dist_tag_test_module_destroy/dist-tags/not-exists') .delete('/-/package/@cnpmtest/dist_tag_test_module_destroy/dist-tags/not-exists')

View File

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

@@ -89,6 +89,13 @@ describe('controllers/registry/package/list.test.js', function () {
}); });
}); });
it('should support jsonp', function (done) {
request(app.listen())
.get('/@cnpmtest/testmodule-list-1?callback=jsonp')
.expect(/jsonp\(\{/)
.expect(200, done);
});
it('should 404 when package not exists', function (done) { it('should 404 when package not exists', function (done) {
request(app.listen()) request(app.listen())
.get('/@cnpmtest/not-exists-package') .get('/@cnpmtest/not-exists-package')
@@ -130,18 +137,17 @@ describe('controllers/registry/package/list.test.js', function () {
describe('unpublished', function () { describe('unpublished', function () {
before(function (done) { before(function (done) {
utils.sync('tfs', done); utils.sync('moduletest1', done);
}); });
it('should show unpublished info', function (done) { it('should show unpublished info', function (done) {
mm(config, 'syncModel', 'all'); mm(config, 'syncModel', 'all');
request(app.listen()) request(app.listen())
.get('/tfs') .get('/moduletest1')
.expect(404, function (err, res) { .expect(404, function (err, res) {
should.not.exist(err); should.not.exist(err);
var data = res.body; var data = res.body;
data.time.unpublished.name.should.equal('fengmk2'); data.time.unpublished.name.should.equal('dead_horse');
data.time.unpublished.description.should.equal('tfs');
done(); 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

@@ -0,0 +1,54 @@
/**!
* Copyright(c) cnpm and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/
'use strict';
/**
* Module dependencies.
*/
const pedding = require('pedding');
const request = require('supertest');
const mm = require('mm');
const app = require('../../../../servers/registry');
const utils = require('../../../utils');
describe('test/controllers/registry/package/list_dependents.test.js', function () {
afterEach(mm.restore);
before(function (done) {
done = pedding(2, done);
const pkg = utils.getPackage('@cnpmtest/testmodule-list-dependents1', '1.0.0', utils.admin);
pkg.versions['1.0.0'].dependencies = {
'@cnpmtest/testmodule-list-dependents2': '~1.0.0',
};
request(app.listen())
.put('/' + pkg.name)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, done);
const pkg2 = utils.getPackage('@cnpmtest/testmodule-list-dependents2', '1.0.0', utils.admin);
request(app.listen())
.put('/' + pkg2.name)
.set('authorization', utils.adminAuth)
.send(pkg2)
.expect(201, done);
});
it('should list package dependents', function (done) {
request(app)
.get('/-/package/@cnpmtest/testmodule-list-dependents2/dependents')
.expect({
dependents: [
'@cnpmtest/testmodule-list-dependents1',
],
})
.expect(200, done);
});
});

View File

@@ -5,7 +5,7 @@
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -23,14 +23,14 @@ var config = require('../../../../config');
var packageService = require('../../../../services/package'); var packageService = require('../../../../services/package');
var nfs = require('../../../../common/nfs'); 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); afterEach(mm.restore);
before(function (done) { before(function (done) {
var pkg = utils.getPackage('@cnpmtest/testmodule-remove-1', '1.0.0', utils.admin); var pkg = utils.getPackage('@cnpmtest/testmodule-remove-1', '1.0.0', utils.otherUser);
request(app.listen()) request(app.listen())
.put('/' + pkg.name) .put('/' + pkg.name)
.set('authorization', utils.adminAuth) .set('authorization', utils.otherUserAuth)
.send(pkg) .send(pkg)
.expect(201, done); .expect(201, done);
}); });
@@ -56,47 +56,13 @@ describe('controllers/registry/package/remove.test.js', function () {
.expect(404, done); .expect(404, done);
}); });
it('should delete 403 when user is not admin on config.enablePrivate = true', function (done) { it('should delete 403 when user is not admin', function (done) {
mm(config, 'enablePrivate', true);
request(app) request(app)
.del('/@cnpmtest/testmodule-remove-1/-rev/1') .del('/@cnpmtest/testmodule-remove-1/-rev/1')
.set('authorization', utils.otherUserAuth) .set('authorization', utils.otherUserAuth)
.expect({ .expect({
error: 'no_perms', error: 'no_perms',
reason: 'Private mode enable, only admin can publish this module' reason: 'Only administrators can unpublish module'
})
.expect(403, done);
});
it('should 400 when scope not exists', function (done) {
request(app)
.del('/@cnpm-not-exists/testmodule-remove-1/-rev/1')
.set('authorization', utils.otherUserAuth)
.expect({
error: 'invalid scope',
reason: 'scope @cnpm-not-exists not match legal scopes: @cnpm, @cnpmtest'
})
.expect(400, done);
});
it('should 403 when delete non scoped package', function (done) {
request(app)
.del('/testmodule-remove-1/-rev/1')
.set('authorization', utils.otherUserAuth)
.expect({
error: 'no_perms',
reason: 'only allow publish with @cnpm, @cnpmtest scope(s)'
})
.expect(403, done);
});
it('should 403 when user not maintainer', function (done) {
request(app)
.del('/@cnpmtest/testmodule-remove-1/-rev/1')
.set('authorization', utils.otherUserAuth)
.expect({
error: 'forbidden user',
reason: 'cnpmjstest101 not authorized to modify @cnpmtest/testmodule-remove-1'
}) })
.expect(403, done); .expect(403, done);
}); });

View File

@@ -27,10 +27,10 @@ describe('controllers/registry/package/remove_version.test.js', function () {
var lastRev; var lastRev;
before(function (done) { before(function (done) {
var pkg = utils.getPackage('@cnpmtest/testmodule-remove_version-1', '0.0.1', utils.admin); var pkg = utils.getPackage('@cnpmtest/testmodule-remove_version-1', '0.0.1', utils.otherUser);
request(app.listen()) request(app.listen())
.put('/' + pkg.name) .put('/' + pkg.name)
.set('authorization', utils.adminAuth) .set('authorization', utils.otherUserAuth)
.send(pkg) .send(pkg)
.expect(201, function (err, res) { .expect(201, function (err, res) {
should.not.exist(err); should.not.exist(err);
@@ -78,7 +78,7 @@ describe('controllers/registry/package/remove_version.test.js', function () {
.expect(401, done); .expect(401, done);
}); });
it('should 403 when auth error', function (done) { it('should 403 when not admin', function (done) {
request(app) request(app)
.del('/@cnpmtest/testmodule-remove_version-1/download/@cnpmtest/testmodule-remove_version-1-0.0.1.tgz/-rev/' + lastRev) .del('/@cnpmtest/testmodule-remove_version-1/download/@cnpmtest/testmodule-remove_version-1-0.0.1.tgz/-rev/' + lastRev)
.set('authorization', utils.otherUserAuth) .set('authorization', utils.otherUserAuth)
@@ -94,10 +94,10 @@ describe('controllers/registry/package/remove_version.test.js', function () {
describe('mock error', function () { describe('mock error', function () {
before(function (done) { before(function (done) {
var pkg = utils.getPackage('@cnpmtest/testmodule-remove_version-1', '0.0.2', utils.admin); var pkg = utils.getPackage('@cnpmtest/testmodule-remove_version-1', '0.0.2', utils.otherUser);
request(app.listen()) request(app.listen())
.put('/' + pkg.name) .put('/' + pkg.name)
.set('authorization', utils.adminAuth) .set('authorization', utils.otherUserAuth)
.send(pkg) .send(pkg)
.expect(201, done); .expect(201, done);
}); });

View File

@@ -1,11 +1,9 @@
/**! /**!
* cnpmjs.org - test/controllers/registry/package/save.test.js
*
* Copyright(c) cnpmjs.org and other contributors. * Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -22,7 +20,7 @@ var app = require('../../../../servers/registry');
var config = require('../../../../config'); var config = require('../../../../config');
var utils = require('../../../utils'); var utils = require('../../../utils');
describe('controllers/registry/package/save.test.js', function () { describe('test/controllers/registry/package/save.test.js', function () {
afterEach(mm.restore); afterEach(mm.restore);
describe('no @scoped package', function () { describe('no @scoped package', function () {
@@ -288,11 +286,11 @@ describe('controllers/registry/package/save.test.js', function () {
}); });
it('should save dependents', function* () { it('should save dependents', function* () {
var names = yield* packageService.listDependents('bytetest2'); var names = yield packageService.listDependents('bytetest2');
names.should.length(2); names.should.length(2);
names.should.eql(['@cnpmtest/testmodule-new-1', '@cnpmtest/testmodule-new-2']); names.should.eql(['@cnpmtest/testmodule-new-1', '@cnpmtest/testmodule-new-2']);
names = yield* packageService.listDependents('@cnpmtest/testmodule-new-1'); names = yield packageService.listDependents('@cnpmtest/testmodule-new-1');
names.should.length(0); names.should.length(0);
}); });
}); });

View File

@@ -1,11 +1,9 @@
/**! /**
* cnpmjs.org - test/controllers/registry/package/show.test.js * Copyright(c) cnpm and other contributors.
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -20,15 +18,32 @@ var mm = require('mm');
var app = require('../../../../servers/registry'); var app = require('../../../../servers/registry');
var utils = require('../../../utils'); var utils = require('../../../utils');
describe('controllers/registry/package/show.test.js', function () { describe('test/controllers/registry/package/show.test.js', function () {
afterEach(mm.restore); afterEach(mm.restore);
before(function (done) { before(function(done) {
var pkg = utils.getPackage('@cnpmtest/testmodule-show', '0.0.1', utils.admin); var pkg = utils.getPackage('@cnpmtest/testmodule-show', '0.0.1', utils.admin);
request(app.listen()) request(app.listen())
.put('/' + pkg.name) .put('/' + pkg.name)
.set('authorization', utils.adminAuth) .set('authorization', utils.adminAuth)
.send(pkg) .send(pkg)
.expect(201, function(err) {
should.not.exist(err);
pkg = utils.getPackage('@cnpmtest/testmodule-show', '1.1.0', utils.admin);
request(app.listen())
.put('/' + pkg.name)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, done);
});
});
before(function(done) {
var pkg = utils.getPackage('@cnpmtest/testmodule-only-beta', '1.0.0-beta.1', utils.admin);
request(app.listen())
.put('/' + pkg.name)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, done); .expect(201, done);
}); });
@@ -45,6 +60,65 @@ describe('controllers/registry/package/show.test.js', function () {
}); });
}); });
it('should return max satisfied package with semver range', function (done) {
request(app.listen())
.get('/@cnpmtest/testmodule-show/^1.0.0')
.expect(200, function (err, res) {
should.not.exist(err);
var data = res.body;
data.name.should.equal('@cnpmtest/testmodule-show');
data.version.should.equal('1.1.0');
data.dist.tarball.should.containEql('/@cnpmtest/testmodule-show/download/@cnpmtest/testmodule-show-1.1.0.tgz');
done();
});
});
it('should return max satisfied package with complex semver range', function (done) {
request(app.listen())
.get('/@cnpmtest/testmodule-show/>1.2.0 <=2 || 0.0.1')
.expect(200, function (err, res) {
should.not.exist(err);
var data = res.body;
data.name.should.equal('@cnpmtest/testmodule-show');
data.version.should.equal('0.0.1');
data.dist.tarball.should.containEql('/@cnpmtest/testmodule-show/download/@cnpmtest/testmodule-show-0.0.1.tgz');
done();
});
});
it('should return max satisfied package with *', function (done) {
request(app.listen())
.get('/@cnpmtest/testmodule-show/*')
.expect(200, function (err, res) {
should.not.exist(err);
var data = res.body;
data.name.should.equal('@cnpmtest/testmodule-show');
data.version.should.equal('1.1.0');
data.dist.tarball.should.containEql('/@cnpmtest/testmodule-show/download/@cnpmtest/testmodule-show-1.1.0.tgz');
done();
});
});
it('should return the only beta version', function (done) {
request(app.listen())
.get('/@cnpmtest/testmodule-only-beta/*')
.expect(200, function (err, res) {
should.not.exist(err);
var data = res.body;
data.name.should.equal('@cnpmtest/testmodule-only-beta');
data.version.should.equal('1.0.0-beta.1');
data.dist.tarball.should.containEql('/@cnpmtest/testmodule-only-beta/download/@cnpmtest/testmodule-only-beta-1.0.0-beta.1.tgz');
done();
});
});
it('should support jsonp', function (done) {
request(app.listen())
.get('/@cnpmtest/testmodule-show/0.0.1?callback=jsonp')
.expect(/jsonp\(\{/)
.expect(200, done);
});
it('should return latest tag', function (done) { it('should return latest tag', function (done) {
request(app.listen()) request(app.listen())
.get('/@cnpmtest/testmodule-show/latest') .get('/@cnpmtest/testmodule-show/latest')
@@ -52,7 +126,7 @@ describe('controllers/registry/package/show.test.js', function () {
should.not.exist(err); should.not.exist(err);
var data = res.body; var data = res.body;
data.name.should.equal('@cnpmtest/testmodule-show'); data.name.should.equal('@cnpmtest/testmodule-show');
data.version.should.equal('0.0.1'); data.version.should.equal('1.1.0');
done(); 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) { .expect(200, function (err, res) {
should.not.exist(err); should.not.exist(err);
res.body.name.should.equal('fengmk2'); 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', res.body._cnpm_meta.should.have.keys('id', 'npm_user', 'custom_user',
'gmt_modified', 'gmt_create'); 'gmt_modified', 'gmt_create');
res.body._cnpm_meta.npm_user.should.equal(true); res.body._cnpm_meta.npm_user.should.equal(true);

View File

@@ -123,13 +123,19 @@ describe('controllers/sync.test.js', function () {
.get('/sync/pedding/log/123123123') .get('/sync/pedding/log/123123123')
.expect(404, done); .expect(404, done);
}); });
it('should 404 when log id not number', function (done) {
request(webApp.listen())
.get('/sync/pedding/log/info.php')
.expect(404, done);
});
}); });
describe('scope package', function () { describe('scope package', function () {
it('should sync scope package not found', function (done) { it('should sync scope package not found', function (done) {
request(webApp.listen()) request(webApp.listen())
.put('/sync/@cnpm/not-exists-package') .put('/sync/@cnpm/not-exists-package')
.expect(404, done); .expect(201, done);
}); });
}); });
}); });

View File

@@ -1,30 +1,23 @@
/**!
* cnpmjs.org - test/controllers/sync_module_worker.test.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict'; 'use strict';
/** /**
* Module dependencies. * Module dependencies.
*/ */
var should = require('should');
var mm = require('mm'); var mm = require('mm');
var thunkify = require('thunkify-wrap'); var thunkify = require('thunkify-wrap');
var request = require('supertest'); var request = require('supertest');
var urllib = require('urllib');
var config = require('../../config'); var config = require('../../config');
var SyncModuleWorker = require('../../controllers/sync_module_worker'); var SyncModuleWorker = require('../../controllers/sync_module_worker');
var logService = require('../../services/module_log'); var logService = require('../../services/module_log');
var packageService = require('../../services/package'); var packageService = require('../../services/package');
var utils = require('../utils'); var utils = require('../utils');
var app = require('../../servers/registry'); var app = require('../../servers/registry');
var User = require('../../models').User;
describe('controllers/sync_module_worker.test.js', function () { describe('test/controllers/sync_module_worker.test.js', function () {
afterEach(mm.restore); afterEach(mm.restore);
beforeEach(function () { beforeEach(function () {
@@ -53,9 +46,9 @@ describe('controllers/sync_module_worker.test.js', function () {
yield end(); yield end();
}); });
it('should not sync scoped package', function* () { it('should not sync private scoped package', function* () {
var worker = new SyncModuleWorker({ var worker = new SyncModuleWorker({
name: '@scoped/google', name: '@cnpmtest/google',
username: 'fengmk2', username: 'fengmk2',
}); });
worker.start(); worker.start();
@@ -63,6 +56,45 @@ describe('controllers/sync_module_worker.test.js', function () {
yield end(); 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',
noDep: true,
});
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* () { it('should start a sync worker and dont sync deps', function* () {
var log = yield* logService.create({ var log = yield* logService.create({
name: 'byte', name: 'byte',
@@ -260,5 +292,64 @@ describe('controllers/sync_module_worker.test.js', function () {
var end = thunkify.event(worker, 'end'); var end = thunkify.event(worker, 'end');
yield end(); yield end();
}); });
describe('sync deleted user', function() {
before(function*() {
var user = {
name: 'notexistsuserscnpmtest',
email: 'notexistsuserscnpmtest@gmail.com',
};
yield User.saveNpmUser(user);
var user = {
name: 'existsuserscnpmtest',
email: 'existsuserscnpmtest@gmail.com',
password_sha: '0',
salt: '0',
ip: '127.0.0.1',
};
yield User.add(user);
});
it('should not delete when cnpm user exists', function*() {
var worker = new SyncModuleWorker({
type: 'user',
name: 'existsuserscnpmtest',
username: 'fengmk2',
});
worker.start();
var end = thunkify.event(worker, 'end');
yield end();
var user = yield User.findByName('existsuserscnpmtest');
should.exists(user);
user.name.should.equal('existsuserscnpmtest');
});
it('should delete when user exists', function*() {
var worker = new SyncModuleWorker({
type: 'user',
name: 'notexistsuserscnpmtest',
username: 'fengmk2',
});
worker.start();
var end = thunkify.event(worker, 'end');
yield end();
var user = yield User.findByName('notexistsuserscnpmtest');
should.not.exists(user);
});
it('should not delete when user not exists', function*() {
var worker = new SyncModuleWorker({
type: 'user',
name: 'notexistsuserscnpmtest',
username: 'fengmk2',
});
worker.start();
var end = thunkify.event(worker, 'end');
yield end();
var user = yield User.findByName('notexistsuserscnpmtest');
should.not.exists(user);
});
});
}); });
}); });

View File

@@ -19,7 +19,7 @@ var should = require('should');
var request = require('supertest'); var request = require('supertest');
var app = require('../../servers/registry'); var app = require('../../servers/registry');
describe('controllers/total.test.js', function () { describe('test/controllers/total.test.js', function () {
describe('GET / in registry', function () { describe('GET / in registry', function () {
it('should return total info', function (done) { it('should return total info', function (done) {
request(app.listen()) request(app.listen())
@@ -29,7 +29,14 @@ describe('controllers/total.test.js', function () {
res.body.db_name.should.equal('registry'); res.body.db_name.should.equal('registry');
res.body.store_engine.should.be.a.String; res.body.store_engine.should.be.a.String;
res.body.node_version.should.equal(process.version); res.body.node_version.should.equal(process.version);
done(); res.body.cache_time.should.be.a.Number;
// request again should get cache total info
request(app.listen())
.get('/')
.expect(function(res2) {
res2.body.cache_time.should.equal(res.body.cache_time);
})
.expect(200, done);
}); });
}); });

View File

@@ -5,7 +5,7 @@
* MIT Licensed * MIT Licensed
* *
* Authors: * Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -20,6 +20,7 @@ var mm = require('mm');
var app = require('../../../servers/web'); var app = require('../../../servers/web');
var registry = require('../../../servers/registry'); var registry = require('../../../servers/registry');
var utils = require('../../utils'); var utils = require('../../utils');
var config = require('../../../config')
describe('controllers/web/badge.test.js', function () { describe('controllers/web/badge.test.js', function () {
afterEach(mm.restore); afterEach(mm.restore);
@@ -35,7 +36,7 @@ describe('controllers/web/badge.test.js', function () {
should.not.exists(err); should.not.exists(err);
request(app) request(app)
.get('/badge/v/@cnpmtest/badge-test-module.svg?style=flat-square') .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', config.badgePrefixURL + '/cnpm-1.0.1-blue.svg?style=flat-square')
.expect(302, done); .expect(302, done);
}); });
}); });
@@ -50,7 +51,22 @@ describe('controllers/web/badge.test.js', function () {
should.not.exists(err); should.not.exists(err);
request(app) request(app)
.get('/badge/v/@cnpmtest/badge-test-module.svg?style=flat-square&tag=v2') .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', config.badgePrefixURL + '/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', config.badgePrefixURL + '/ant--design-3.0.1-blue.svg?style=flat-square')
.expect(302, done); .expect(302, done);
}); });
}); });
@@ -65,7 +81,7 @@ describe('controllers/web/badge.test.js', function () {
should.not.exists(err); should.not.exists(err);
request(app) request(app)
.get('/badge/v/@cnpmtest/badge-test-module.svg?style=flat-square') .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', config.badgePrefixURL + '/cnpm-1.0.0--beta1-blue.svg?style=flat-square')
.expect(302, done); .expect(302, done);
}); });
}); });
@@ -80,7 +96,7 @@ describe('controllers/web/badge.test.js', function () {
should.not.exists(err); should.not.exists(err);
request(app) request(app)
.get('/badge/v/@cnpmtest/badge-test-module.svg?style=flat-square') .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', config.badgePrefixURL + '/cnpm-0.1.0-green.svg?style=flat-square')
.expect(302, done); .expect(302, done);
}); });
}); });
@@ -95,7 +111,7 @@ describe('controllers/web/badge.test.js', function () {
should.not.exists(err); should.not.exists(err);
request(app) request(app)
.get('/badge/v/@cnpmtest/badge-test-module.svg?style=flat-square') .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', config.badgePrefixURL + '/cnpm-0.0.0-red.svg?style=flat-square')
.expect(302, done); .expect(302, done);
}); });
}); });
@@ -103,8 +119,25 @@ describe('controllers/web/badge.test.js', function () {
it('should show invalid when package not exists', function (done) { it('should show invalid when package not exists', function (done) {
request(app) request(app)
.get('/badge/v/@cnpmtest/badge-test-module-not-exists.svg?style=flat') .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', config.badgePrefixURL + '/cnpm-invalid-lightgrey.svg?style=flat')
.expect(302, done); .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', config.badgePrefixURL + '/downloads-0-brightgreen.svg?style=flat-square')
.expect(302, done);
});
});
});
}); });

View File

@@ -6,6 +6,7 @@
* *
* Authors: * Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me) * dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <m@fengmk2.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -49,9 +50,23 @@ describe('controllers/web/package/search.test.js', function () {
request(app) request(app)
.get('/browse/keyword/@cnpmtest/testmodule-web-search?type=json') .get('/browse/keyword/@cnpmtest/testmodule-web-search?type=json')
.expect(200) .expect(200)
.expect({
keyword: '@cnpmtest/testmodule-web-search',
match: { name: '@cnpmtest/testmodule-web-search', description: '' },
packages: [ { name: '@cnpmtest/testmodule-web-search', description: '' } ],
keywords: []
})
.expect('content-type', 'application/json; charset=utf-8', done); .expect('content-type', 'application/json; charset=utf-8', done);
}); });
it('should search with jsonp work', function (done) {
request(app)
.get('/browse/keyword/@cnpmtest/testmodule-web-search?type=json&callback=foo')
.expect(200)
.expect('/**/ typeof foo === \'function\' && foo({"keyword":"@cnpmtest/testmodule-web-search","match":{"name":"@cnpmtest/testmodule-web-search","description":""},"packages":[{"name":"@cnpmtest/testmodule-web-search","description":""}],"keywords":[]});')
.expect('content-type', 'application/javascript; charset=utf-8', done);
});
it('should list no match ok', function (done) { it('should list no match ok', function (done) {
request(app) request(app)
.get('/browse/keyword/notexistpackage') .get('/browse/keyword/notexistpackage')

View File

@@ -1,28 +1,14 @@
/*!
* cnpmjs.org - test/controllers/web/package/show.test.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
'use strict'; 'use strict';
/**
* Module dependencies.
*/
var should = require('should'); var should = require('should');
var request = require('supertest'); var request = require('supertest-as-promised');
var mm = require('mm'); var mm = require('mm');
var config = require('../../../../config'); var config = require('../../../../config');
var app = require('../../../../servers/web'); var app = require('../../../../servers/web');
var registry = require('../../../../servers/registry'); var registry = require('../../../../servers/registry');
var utils = require('../../../utils'); var utils = require('../../../utils');
describe('controllers/web/package/show.test.js', function () { describe('controllers/web/package/show.test.js', () => {
before(function (done) { before(function (done) {
var pkg = utils.getPackage('@cnpmtest/testmodule-web-show', '0.0.1', utils.admin); var pkg = utils.getPackage('@cnpmtest/testmodule-web-show', '0.0.1', utils.admin);
pkg.versions['0.0.1'].dependencies = { pkg.versions['0.0.1'].dependencies = {
@@ -113,17 +99,17 @@ describe('controllers/web/package/show.test.js', function () {
}); });
}); });
describe('unpublished package', function () { describe('unpublished package', () => {
before(function (done) { before(done => {
utils.sync('tnpm', done); utils.sync('mk2testmodule', done);
}); });
it('should display unpublished info', function (done) { it('should display unpublished info', () => {
mm(config, 'syncModel', 'all'); mm(config, 'syncModel', 'all');
request(app) return request(app)
.get('/package/tnpm') .get('/package/mk2testmodule')
.expect(200) .expect(200)
.expect(/This package has been unpublished\./, done); .expect(/This package has been unpublished\./);
}); });
}); });
@@ -149,20 +135,22 @@ describe('controllers/web/package/show.test.js', function () {
}); });
}); });
describe('show npm package', function () { describe('show npm package', () => {
before(function (done) { before(done => {
mm(config, 'syncModel', 'exists'); mm(config, 'syncModel', 'exists');
utils.sync('pedding', done); utils.sync('pedding', done);
}); });
it('should show pedding package info and contributors', function (done) { it('should show pedding package info and contributors', () => {
mm(config, 'syncModel', 'exists'); mm(config, 'syncModel', 'exists');
request(app) return request(app)
.get('/package/pedding') .get('/package/pedding')
.expect(200) .expect(200)
// https://github.com/cnpm/cnpmjs.org/issues/497 // https://github.com/cnpm/cnpmjs.org/issues/497
.expect(/by <a href="\/\~fengmk2">fengmk2<\/a>/) .expect(/by <a href="\/\~fengmk2">fengmk2<\/a>/)
.expect(/pedding/, done); // snyk link
.expect(/<a class="badge-link" href="https:\/\/snyk\.io\/test\/npm\/pedding" target="_blank"><img title="Known Vulnerabilities" src="https:\/\/snyk\.io\/test\/npm\/pedding\/badge\.svg\?style=flat-square"><\/a>/)
.expect(/pedding/);
}); });
}); });
}); });

9
test/fixtures/code.md vendored Normal file
View File

@@ -0,0 +1,9 @@
# test < >
```html
<body>hi</body>
```
```js
var a = 1 > 2 ? 'a' : 'b';
```

357
test/fixtures/eat-cpu-all.md vendored Normal file
View File

@@ -0,0 +1,357 @@
# BOWER [![Build Status](https://secure.travis-ci.org/bower/bower.png?branch=master)](http://travis-ci.org/bower/bower)
Bower is a package manager for the web. It offers a generic, unopinionated
solution to the problem of **front-end package management**, while exposing the
package dependency model via an API that can be consumed by a more opinionated
build stack. There are no system wide dependencies, no dependencies are shared
between different apps, and the dependency tree is flat.
Bower runs over Git, and is package-agnostic. A packaged component can be made
up of any type of asset, and use any type of transport (e.g., AMD, CommonJS,
etc.).
[View all packages available through Bower's registry](http://sindresorhus.com/bower-components/).
## Installing Bower
Bower depends on [Node](http://nodejs.org/) and [npm](http://npmjs.org/). It's
installed globally using npm:
```
npm install -g bower
```
Also make sure that [git](http://git-scm.com/) is installed as some bower
packages require it to be fetched and installed.
## Usage
Much more information is available via `bower help` once it's installed. This
is just enough to get you started.
#### Warning
On `prezto` or `oh-my-zsh`, do not forget to `alias bower='noglob bower'` or `bower install jquery\#1.9.1`
#### Running commands with sudo
Bower is a user command, there is no need to execute it with superuser permissions.
However, if you still want to run commands with sudo, use `--allow-root` option.
### Installing packages and dependencies
Bower offers several ways to install packages:
```
# Using the dependencies listed in the current directory's bower.json
bower install
# Using a local or remote package
bower install <package>
# Using a specific version of a package
bower install <package>#<version>
# Using a different name and a specific version of a package
bower install <name>=<package>#<version>
```
Where `<package>` can be any one of the following:
* A name that maps to a package registered with Bower, e.g, `jquery`. ‡
* A remote Git endpoint, e.g., `git://github.com/someone/some-package.git`. Can be
public or private. ‡
* A local endpoint, i.e., a folder that's a Git repository. ‡
* A shorthand endpoint, e.g., `someone/some-package` (defaults to GitHub). ‡
* A URL to a file, including `zip` and `tar` files. Its contents will be
extracted.
‡ These types of `<package>` might have versions available. You can specify a
[semver](http://semver.org/) compatible version to fetch a specific release, and lock the
package to that version. You can also use ranges to specify a range of versions.
All package contents are installed in the `bower_components` directory by default.
You should **never** directly modify the contents of this directory.
Using `bower list` will show all the packages that are installed locally.
**N.B.** If you aren't authoring a package that is intended to be consumed by
others (e.g., you're building a web app), you should always check installed
packages into source control.
### Finding packages
To search for packages registered with Bower:
```
bower search [<name>]
```
Using just `bower search` will list all packages in the registry.
### Using packages
The easiest approach is to use Bower statically, just reference the package's
installed components manually using a `script` tag:
```html
<script src="/bower_components/jquery/index.js"></script>
```
For more complex projects, you'll probably want to concatenate your scripts or
use a module loader. Bower is just a package manager, but there are plenty of
other tools -- such as [Sprockets](https://github.com/sstephenson/sprockets)
and [RequireJS](http://requirejs.org/) -- that will help you do this.
### Registering packages
To register a new package:
* There **must** be a valid manifest JSON in the current working directory.
* Your package should use [semver](http://semver.org/) Git tags.
* Your package **must** be available at a Git endpoint (e.g., GitHub); remember
to push your Git tags!
Then use the following command:
```
bower register <my-package-name> <git-endpoint>
```
The Bower registry does not have authentication or user management at this point
in time. It's on a first come, first served basis. Think of it like a URL
shortener. Now anyone can run `bower install <my-package-name>`, and get your
library installed.
There is no direct way to unregister a package yet. For now, you can [request a
package be unregistered](https://github.com/bower/bower/issues/120).
### Uninstalling packages
To uninstall a locally installed package:
```
bower uninstall <package-name>
```
## Configuration
Bower can be configured using JSON in a `.bowerrc` file.
The current spec can be read
[here](https://docs.google.com/document/d/1APq7oA9tNao1UYWyOm8dKqlRP2blVkROYLZ2fLIjtWc/edit#heading=h.4pzytc1f9j8k)
in the `Configuration` section.
## Defining a package
You must create a `bower.json` in your project's root, and specify all of its
dependencies. This is similar to Node's `package.json`, or Ruby's `Gemfile`,
and is useful for locking down a project's dependencies.
*NOTE:* In versions of Bower before 0.9.0 the package metadata file was called
`component.json` rather than `bower.json`. This has changed to avoid a name
clash with another tool. You can still use `component.json` for now but it is
deprecated and the automatic fallback is likely to be removed in an upcoming
release.
You can interactively create a `bower.json` with the following command:
```
bower init
```
The `bower.json` defines several options:
* `name` (required): The name of your package.
* `version`: A semantic version number (see [semver](http://semver.org/)).
* `main` [string|array]: The primary endpoints of your package.
* `ignore` [array]: An array of paths not needed in production that you want
Bower to ignore when installing your package.
* `dependencies` [hash]: Packages your package depends upon in production.
* `devDependencies` [hash]: Development dependencies.
* `private` [boolean]: Set to true if you want to keep the package private and
do not want to register the package in future.
```json
{
"name": "my-project",
"version": "1.0.0",
"main": "path/to/main.css",
"ignore": [
".jshintrc",
"**/*.txt"
],
"dependencies": {
"<name>": "<version>",
"<name>": "<folder>",
"<name>": "<package>"
},
"devDependencies": {
"<test-framework-name>": "<version>"
}
}
```
## Consuming a package
Bower also makes available a source mapping. This can be used by build tools to
easily consume Bower packages.
If you pass the `--paths` option to Bower's `list` command, you will get a
simple path-to-name mapping:
```json
{
"backbone": "bower_components/backbone/index.js",
"jquery": "bower_components/jquery/index.js",
"underscore": "bower_components/underscore/index.js"
}
```
Alternatively, every command supports the `--json` option that makes bower
output JSON. Command result is outputted to `stdout` and error/logs to
`stderr`.
## Programmatic API
Bower provides a powerful, programmatic API. All commands can be accessed
through the `bower.commands` object.
```js
var bower = require('bower');
bower.commands
.install(['jquery'], { save: true }, { /* custom config */ })
.on('end', function (installed) {
console.log(installed);
});
bower.commands
.search('jquery', {})
.on('end', function (results) {
console.log(results);
});
```
Commands emit four types of events: `log`, `prompt`, `end`, `error`.
* `log` is emitted to report the state/progress of the command.
* `prompt` is emitted whenever the user needs to be prompted.
* `error` will only be emitted if something goes wrong.
* `end` is emitted when the command successfully ends.
For a better of idea how this works, you may want to check out [our bin
file](https://github.com/bower/bower/blob/master/bin/bower).
When using bower programmatically, prompting is disabled by default. Though you can enable it when calling commands with `interactive: true` in the config.
This requires you to listen for the `prompt` event and handle the prompting yourself. The easiest way is to use the [inquirer](https://npmjs.org/package/inquirer) npm module like so:
```js
var inquirer = require('inquirer');
bower.commands
.install(['jquery'], { save: true }, { interactive: true })
// ..
.on('prompt', function (prompts, callback) {
inquirer.prompt(prompts, callback);
});
```
## Completion (experimental)
_NOTE_: Completion is still not implemented for the 1.0.0 release
Bower now has an experimental `completion` command that is based on, and works
similarly to the [npm completion](https://npmjs.org/doc/completion.html). It is
not available for Windows users.
This command will output a Bash / ZSH script to put into your `~/.bashrc`,
`~/.bash_profile`, or `~/.zshrc` file.
```
bower completion >> ~/.bash_profile
```
## A note for Windows users
To use Bower on Windows, you must install
[msysgit](http://code.google.com/p/msysgit/) correctly. Be sure to check the
option shown below:
![msysgit](http://f.cl.ly/items/2V2O3i1p3R2F1r2v0a12/mysgit.png)
Note that if you use TortoiseGit and if Bower keeps asking for your SSH
password, you should add the following environment variable: `GIT_SSH -
C:\Program Files\TortoiseGit\bin\TortoisePlink.exe`. Adjust the `TortoisePlink`
path if needed.
## Contact
Have a question?
* [StackOverflow](http://stackoverflow.com/questions/tagged/bower)
* [Mailinglist](http://groups.google.com/group/twitter-bower) - twitter-bower@googlegroups.com
* [\#bower](http://webchat.freenode.net/?channels=bower) on Freenode
## Contributing to this project
Anyone and everyone is welcome to contribute. Please take a moment to
review the [guidelines for contributing](CONTRIBUTING.md).
* [Bug reports](CONTRIBUTING.md#bugs)
* [Feature requests](CONTRIBUTING.md#features)
* [Pull requests](CONTRIBUTING.md#pull-requests)
## Authors
* [@fat](https://github.com/fat)
* [@maccman](https://github.com/maccman)
* [@satazor](https://github.com/satazor)
Thanks for assistance and contributions:
[@addyosmani](https://github.com/addyosmani),
[@angus-c](https://github.com/angus-c),
[@borismus](https://github.com/borismus),
[@carsonmcdonald](https://github.com/carsonmcdonald),
[@chriseppstein](https://github.com/chriseppstein),
[@danwrong](https://github.com/danwrong),
[@davidmaxwaterman](https://github.com/davidmaxwaterman),
[@desandro](https://github.com/desandro),
[@hemanth](https://github.com/hemanth),
[@isaacs](https://github.com/isaacs),
[@josh](https://github.com/josh),
[@jrburke](https://github.com/jrburke),
[@marcelombc](https://github.com/marcelombc),
[@marcooliveira](https://github.com/marcooliveira),
[@mklabs](https://github.com/mklabs),
[@MrDHat](https://github.com/MrDHat),
[@necolas](https://github.com/necolas),
[@paulirish](https://github.com/paulirish),
[@richo](https://github.com/richo),
[@rvagg](https://github.com/rvagg),
[@sindresorhus](https://github.com/sindresorhus),
[@SlexAxton](https://github.com/SlexAxton),
[@sstephenson](https://github.com/sstephenson),
[@svnlto](https://github.com/svnlto),
[@tomdale](https://github.com/tomdale),
[@uzquiano](https://github.com/uzquiano),
[@visionmedia](https://github.com/visionmedia),
[@wagenet](https://github.com/wagenet),
[@wibblymat](https://github.com/wibblymat),
[@wycats](https://github.com/wycats)
## License
Copyright 2012 Twitter, Inc.
Licensed under the MIT License

25
test/fixtures/eat-cpu.md vendored Normal file
View File

@@ -0,0 +1,25 @@
[@addyosmani](https://github.com/addyosmani),
[@borismus](https://github.com/borismus),
[@carsonmcdonald](https://github.com/carsonmcdonald),
[@chriseppstein](https://github.com/chriseppstein),
[@danwrong](https://github.com/danwrong),
[@davidmaxwaterman](https://github.com/davidmaxwaterman),
[@desandro](https://github.com/desandro),
[@hemanth](https://github.com/hemanth),
[@isaacs](https://github.com/isaacs),
[@josh](https://github.com/josh),
[@jrburke](https://github.com/jrburke),
[@marcelombc](https://github.com/marcelombc),
[@marcooliveira](https://github.com/marcooliveira),
[@mklabs](https://github.com/mklabs),
[@MrDHat](https://github.com/MrDHat),
[@necolas](https://github.com/necolas),
[@paulirish](https://github.com/paulirish),
[@richo](https://github.com/richo),
[@rvagg](https://github.com/rvagg),
[@sindresorhus](https://github.com/sindresorhus),
[@SlexAxton](https://github.com/SlexAxton),
[@sstephenson](https://github.com/sstephenson),
[@svnlto](https://github.com/svnlto),
[@tomdale](https://github.com/tomdale),
[@uzquiano](https://github.com/uzquiano),

View File

@@ -1,13 +1,3 @@
/**!
* cnpmjs.org - test/init.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict'; 'use strict';
/** /**
@@ -20,6 +10,14 @@ if (process.env.DB) {
config.database.dialect = process.env.DB; config.database.dialect = process.env.DB;
} }
if (process.env.DB_PORT) {
config.database.port = parseInt(process.env.DB_PORT);
}
if (process.env.DB_USER) {
config.database.username = process.env.DB_USER;
}
if (process.env.CNPM_SOURCE_NPM) { if (process.env.CNPM_SOURCE_NPM) {
config.sourceNpmRegistry = process.env.CNPM_SOURCE_NPM; config.sourceNpmRegistry = process.env.CNPM_SOURCE_NPM;
} }

View File

@@ -1,13 +1,3 @@
/**!
* cnpmjs.org - test/init_db.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict'; 'use strict';
/** /**
@@ -22,7 +12,8 @@ var config = require('../config');
// init db first // init db first
var initscript = path.join(__dirname, '..', 'models', 'init_script.js'); var initscript = path.join(__dirname, '..', 'models', 'init_script.js');
var cmd = ['node', '--harmony', initscript, 'true', config.database.dialect].join(' '); var cmd = ['node', initscript, 'true',
config.database.dialect, config.database.port, config.database.username].join(' ');
console.log('$ %s', cmd); console.log('$ %s', cmd);
var stdout = childProcess.execSync(cmd); var stdout = childProcess.execSync(cmd);
process.stdout.write(stdout); process.stdout.write(stdout);
@@ -37,6 +28,7 @@ var usernames = [
'cnpmjstest10', // admin 'cnpmjstest10', // admin
'cnpmjstestAdmin2', // other admin 'cnpmjstestAdmin2', // other admin
'cnpmjstestAdmin3', // other admin 'cnpmjstestAdmin3', // other admin
'cnpmjstest_list_by_user',
]; ];
var count = usernames.length; var count = usernames.length;
@@ -56,6 +48,7 @@ usernames.forEach(function (name) {
process.exit(0); process.exit(0);
} }
}).catch(function (err) { }).catch(function (err) {
throw err; console.log(err);
process.exit(1);
}); });
}); });

View File

@@ -6,7 +6,7 @@
* *
* Authors: * Authors:
* dead_horse <dead_horse@qq.com> * dead_horse <dead_horse@qq.com>
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com) * fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
*/ */
'use strict'; 'use strict';
@@ -17,7 +17,7 @@
var common = require('../../lib/common'); var common = require('../../lib/common');
describe('lib/common.test.js', function () { describe('test/lib/common.test.js', function () {
describe('isAdmin()', function () { describe('isAdmin()', function () {
it('should admin is admin', function () { it('should admin is admin', function () {
common.isAdmin('admin').should.equal(true); common.isAdmin('admin').should.equal(true);
@@ -26,4 +26,13 @@ describe('lib/common.test.js', function () {
common.isAdmin('toString').should.equal(false); 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');
});
});
}); });

Some files were not shown because too many files have changed in this diff Show More