Compare commits

...

237 Commits
0.5.1 ... 1.7.3

Author SHA1 Message Date
fengmk2
5bb836381f Release 1.7.3 2014-11-12 16:59:14 +08:00
fengmk2
db73b9a675 fix(sync): should not sync package when maintainers sort change
Closes #500
2014-11-12 16:57:37 +08:00
fengmk2
6ca71e1d8b Release 1.7.2 2014-10-31 14:28:30 +08:00
Yiyu He
ff811ab15e Merge pull request #487 from cnpm/min-sync-interval
feat(sync): add min sync interval time detect
2014-10-31 14:21:39 +08:00
fengmk2
ae1ca1bc32 feat(sync): add min sync interval time detect
Forbidden some guy write `config.syncInterval = 1` to let sync too fast.

Closes #486
2014-10-31 14:18:58 +08:00
Yiyu He
de7bef05c2 fix badge 2014-10-25 10:18:51 +08:00
dead_horse
c03be9c69d add gitter 2014-10-25 10:13:45 +08:00
dead_horse
d2949c9450 Release 1.7.1 2014-10-15 02:43:01 +08:00
dead_horse
7e583b2edf fix typo in sync popular, fix #477 2014-10-15 02:42:24 +08:00
dead_horse
9b13c184fe Release 1.7.0 2014-10-15 02:26:35 +08:00
Yiyu He
1830301ae6 Merge pull request #475 from KidkArolis/configurable-short-registry-url
If sourceNpmRegistry is cnpm - use it in getShort
2014-10-15 02:22:56 +08:00
Karolis Narkevicius
7774a5c6d9 If sourceNpmRegistry is cnpm - use it in getShort
Only fallback to r.cnpmjs.org in case sourceNpmRegistry is not cnpm
2014-10-14 19:21:11 +01:00
fengmk2
05c6d7b4f6 Merge pull request #472 from cnpm/issue468-upon
add sync popular modules, close #468
2014-10-13 11:54:39 +08:00
dead_horse
2a8bd139d0 fix test label 2014-10-13 00:48:37 +08:00
dead_horse
ea322c3310 add sync popular modules, close #468 2014-10-13 00:42:06 +08:00
fengmk2
d48cf497cf add sendmail test script 2014-10-09 10:58:24 +08:00
fengmk2
abaac6089a Release 1.6.1 2014-10-09 01:15:09 +08:00
fengmk2
e8ba8eadb3 Merge pull request #466 from cnpm/sync-npm-package-maintainers
save npm original package maintainers to npm_module_maintainer table.
2014-10-09 00:46:29 +08:00
fengmk2
20093741eb make test on travis faster 2014-10-09 00:33:50 +08:00
fengmk2
51ddf2cf02 ensure not sync user also has his own package names 2014-10-09 00:24:14 +08:00
fengmk2
26efecd2fc add v1.6.x-upgrade sql 2014-10-09 00:01:50 +08:00
fengmk2
678c85102e save npm original package maintainers to npm_module_maintainer table. fixed #464 2014-10-09 00:01:14 +08:00
fengmk2
439b6663f3 use simple 404 2014-10-08 18:40:23 +08:00
fengmk2
3069179718 fix possible xss on 404 page 2014-10-08 18:29:13 +08:00
fengmk2
f2613edcd5 Release 1.6.0 2014-10-08 11:15:44 +08:00
dead_horse
6e9ceaf844 Merge pull request #463 from cnpm/by-user-api
list user all packages api. fixed #462
2014-10-08 01:06:14 +08:00
fengmk2
188dab4ed8 list user all packages api. fixed #462 2014-10-08 00:53:20 +08:00
dead_horse
5d0d742e85 Merge pull request #461 from cnpm/node-dev
add node-dev: $ make dev
2014-10-04 01:56:15 +08:00
fengmk2
92a828c04d update readme 2014-10-04 00:33:27 +08:00
fengmk2
3c804758e7 add node-dev: $ make dev 2014-10-04 00:26:08 +08:00
fengmk2
683b2e3640 Merge pull request #459 from cnpm/sync
always start sync worker
2014-10-03 21:11:39 +08:00
dead_horse
35d445cd11 always start sync worker 2014-10-03 02:06:44 +08:00
fengmk2
45a9e0ff97 Merge pull request #458 from cnpm/nodemailer
update node mailer
2014-10-02 09:14:51 +08:00
dead_horse
dda2f0785c update node mailer 2014-10-02 01:58:43 +08:00
dead_horse
ce02d2f48e update autod 2014-10-02 01:10:03 +08:00
fengmk2
c36523da17 Release 1.5.5 2014-09-25 00:00:46 +08:00
fengmk2
93f33a98cf fix sync in web 2014-09-24 23:44:03 +08:00
dead_horse
c8372f06a8 Merge pull request #454 from cnpm/sync-upstream-on-frist-package
sync upstream only the first package. ...
2014-09-24 23:38:25 +08:00
fengmk2
234e8bf27b sync upstream only the first package. make sync devDependencies optionsal, default is false 2014-09-24 23:34:35 +08:00
fengmk2
dfe56b6222 Merge pull request #453 from cnpm/config
add some comment, default sourceNpmRegistryIsCNpm to true
2014-09-24 23:14:13 +08:00
dead_horse
8548104ee6 add some comment, default sourceNpmRegistryIsCNpm to true 2014-09-24 23:10:13 +08:00
fengmk2
ceeb617f0d Release 1.5.4 2014-09-24 23:04:31 +08:00
fengmk2
91ee61c997 format sync log 2014-09-24 23:04:15 +08:00
fengmk2
1d94ba4e11 Release 1.5.3 2014-09-24 22:23:32 +08:00
dead_horse
5b09d974fb Merge pull request #452 from cnpm/sync-upstream-package
support sync upstream first. fixed #451
2014-09-24 19:54:36 +08:00
fengmk2
763224bbbb support sync upstream first. fixed #451 2014-09-24 19:43:51 +08:00
fengmk2
7b6c757b83 Release 1.5.2 2014-09-24 18:22:36 +08:00
fengmk2
fd915e0c5d support im url on user profile page 2014-09-24 18:21:31 +08:00
dead_horse
fb39eb8c2a Release 1.5.1 2014-09-23 17:15:17 +08:00
dead_horse
f139cccad0 search support case insensitive, close #450 2014-09-23 17:14:03 +08:00
fengmk2
4bb2e6a5d8 Merge pull request #449 from cnpm/sync-in-web
add config._syncInWeb, close #448
2014-09-23 09:20:06 +08:00
dead_horse
4d3c257474 add config._syncInWeb, close #448 2014-09-23 02:02:56 +08:00
dead_horse
b990bd9cf1 Merge pull request #445 from cnpm/show-maintainers-when-publish-fail
show maintainers when publish 403. fixed #430
2014-09-16 01:13:21 +08:00
fengmk2
917b407d1e show maintainers when publish 403. fixed #430 2014-09-16 01:05:40 +08:00
dead_horse
774fd44b12 Merge pull request #444 from cnpm/no-attachment-for-html
no attachment for html
2014-09-15 17:12:45 +08:00
fengmk2
a6b67f5453 no attachment for html 2014-09-15 17:10:49 +08:00
fengmk2
1e56c77deb Release 1.5.0 2014-09-15 16:50:20 +08:00
dead_horse
e1e6b6b674 Merge pull request #441 from cnpm/dist-sync-document
dist sync document too. fixed #420
2014-09-14 18:30:27 +08:00
fengmk2
615d16165c dist sync document too. fixed #420 2014-09-13 00:33:20 +08:00
fengmk2
8986503005 Release 1.4.4 2014-09-12 15:37:57 +08:00
fengmk2
4b5a49b626 badge version support 1.0.0-beta1. fixed #440 2014-09-12 11:54:58 +08:00
fengmk2
fce0876295 Release 1.4.3 2014-09-09 12:06:53 +08:00
fengmk2
9df81764a9 alias /:name/-/:file to /:name/download/:file. fixed #439 2014-09-09 12:06:05 +08:00
dead_horse
4801f15768 Release 1.4.2 2014-09-03 13:12:53 +08:00
dead_horse
d24422fdba change default source registry to taobao's registry 2014-09-03 13:11:55 +08:00
dead_horse
835f898eb3 Merge pull request #435 from cnpm/bluebird
add bluebird
2014-09-03 10:17:02 +08:00
fengmk2
2a3a0436e7 add bluebird 2014-09-03 10:12:05 +08:00
dead_horse
0a782ea014 bump fs-cnpm 2014-09-03 09:38:54 +08:00
dead_horse
3813827d77 Merge pull request #434 from cnpm/agent-stat
show agent sockets stat. fixed #433
2014-09-03 09:35:51 +08:00
fengmk2
264544c780 show agent sockets stat. fixed #433 2014-09-03 00:41:04 +08:00
dead_horse
416afb2e9a update readme 2014-09-02 20:51:25 +08:00
dead_horse
fe15eb78e9 remove pic in readme 2014-09-02 20:50:47 +08:00
dead_horse
64d2facef3 Release 1.4.1 2014-08-20 23:19:52 +08:00
dead_horse
d677e804b5 Merge branch 'master' of github.com:cnpm/cnpmjs.org 2014-08-20 23:18:35 +08:00
dead_horse
f4dae42589 fix login error status 2014-08-20 23:18:24 +08:00
fengmk2
71452f692e Release 1.4.0 2014-08-20 10:01:48 +08:00
dead_horse
c24526d7ff Merge pull request #429 from cnpm/badge
Badge version image
2014-08-20 09:56:48 +08:00
fengmk2
43dcd4b4f1 update readme for badge url desc 2014-08-20 09:53:37 +08:00
fengmk2
ff90c0f238 different version, different color badge 2014-08-20 09:47:06 +08:00
fengmk2
f85752e4bc add badge on package page 2014-08-20 00:25:26 +08:00
fengmk2
4b20c13a4f add version badge. fixed #427 2014-08-20 00:07:53 +08:00
dead_horse
ef61605e0c add download and node version badge 2014-08-18 21:32:52 +08:00
dead_horse
a5291d95dd Release 1.3.2 2014-08-18 11:45:25 +08:00
dead_horse
9de10e791c remove unused eventproxy 2014-08-18 11:27:25 +08:00
dead_horse
1162ed95b0 add custom config in tools/sync_not_exist.js 2014-08-18 11:25:09 +08:00
dead_horse
6cdbc0b058 Release 1.3.1 2014-08-18 10:15:49 +08:00
dead_horse
dc7239637d add sync not exist tools, close #424 2014-08-18 10:14:38 +08:00
dead_horse
b735c9c15c use gittip instand of alipay. close #425 2014-08-17 22:21:59 +08:00
dead_horse
417e19a330 update registry api doc 2014-08-16 16:01:06 +08:00
dead_horse
40ab0929e1 Release 1.3.0 2014-08-11 11:46:58 +08:00
dead_horse
6dabe60839 ignore config/config.js 2014-08-11 11:46:29 +08:00
fengmk2
2680512c71 Merge pull request #421 from cnpm/qn-cnpm
use simple fs storage in develop env
2014-08-10 08:59:40 +08:00
dead_horse
f444566a23 fix test case 2014-08-10 01:57:58 +08:00
dead_horse
99cb110ee2 use fs-cnpm 2014-08-10 01:27:08 +08:00
dead_horse
536704705c fix test 2014-08-09 21:32:36 +08:00
dead_horse
d919a81dae use qn-cnpm 2014-08-09 21:17:59 +08:00
dead_horse
fe8100ea30 bump cfork 2014-08-09 20:16:56 +08:00
dead_horse
764c32646e Release 1.2.2 2014-08-08 10:47:50 +08:00
dead_horse
dea16e72de bump koa 2014-08-08 10:45:50 +08:00
fengmk2
7f951a6c5d Release 1.2.1 2014-08-07 18:19:08 +08:00
fengmk2
a1f2bc87b7 Merge pull request #418 from cnpm/undeprecate
deprecated bug fix and support undeprecate
2014-08-07 18:18:31 +08:00
fengmk2
706a5e053b deprecated bug fix and support undeprecate 2014-08-07 18:06:32 +08:00
fengmk2
eddc1a1bb9 Release 1.2.0 2014-08-07 17:27:39 +08:00
dead_horse
6e5abe44b9 Merge pull request #417 from cnpm/deprecate-api
Deprecate API
2014-08-07 17:20:30 +08:00
fengmk2
e6484e7e7b show deprecated message 2014-08-07 17:18:44 +08:00
fengmk2
7a88fa49d2 Sync deprecated field if it missing 2014-08-07 16:48:41 +08:00
fengmk2
82189bd393 Support $ cnpm deprecate [pkgname]@[version] "message". fixed #415 2014-08-07 16:24:35 +08:00
fengmk2
7834f44901 Release 1.1.0 2014-08-07 10:18:39 +08:00
dead_horse
463cf44133 Merge pull request #414 from cnpm/add-maintainers-bugfix
Add user to maintainers when publish. fixed #395
2014-08-07 09:21:17 +08:00
fengmk2
e6bb4b76c2 Add user to maintainers when publish. fixed #395
Also remove the maintainers when package unpublish.
2014-08-07 00:32:10 +08:00
fengmk2
b86d8eb110 List all npm registry api. close #413 2014-08-06 00:49:28 +08:00
dead_horse
070e72b571 Merge pull request #412 from cnpm/limit-list-since
limit list since
2014-08-05 09:24:16 +08:00
fengmk2
6e6f87c147 limit list since 2014-08-05 01:51:19 +08:00
dead_horse
bc9306eda7 Merge pull request #411 from cnpm/deps-change
change deps by "~"
2014-08-04 23:30:02 +08:00
fengmk2
66ea71756c change deps by "~" 2014-08-04 23:25:12 +08:00
dead_horse
0a1fff70cc Merge pull request #409 from cnpm/cfork
use cfork to make sure worker fork and restart
2014-08-04 21:08:05 +08:00
fengmk2
71da60c16f use cfork to make sure worker fork and restart 2014-08-04 18:53:55 +08:00
dead_horse
b5d585988f Merge pull request #405 from cnpm/master-uncaughtException
handle master uncaughtException. fixed #403
2014-08-04 12:27:41 +08:00
fengmk2
758d1d4320 handle master uncaughtException. fixed #403 2014-08-04 12:26:26 +08:00
fengmk2
7b77f09264 Release 1.0.6 2014-08-02 02:13:44 +08:00
fengmk2
7731b44ab4 WTF moment@2.8.0 missing 2014-08-02 02:13:24 +08:00
fengmk2
4bc6998040 Release 1.0.5 2014-08-02 02:04:36 +08:00
fengmk2
0d28fbde54 fix remove tar test cases 2014-08-02 02:03:59 +08:00
fengmk2
660035c1d5 Merge pull request #401 from cnpm/hotfix-unpublish-version
unpublish pkg@version bug hotfix. fixed #400
2014-08-02 01:57:19 +08:00
fengmk2
f21e5a1c07 unpublish pkg@version bug hotfix. fixed #400 2014-08-02 01:56:32 +08:00
fengmk2
4d20d91965 Release 1.0.4 2014-08-01 18:40:45 +08:00
fengmk2
a3e33850fc hotfix #399 use not exists 2014-08-01 18:39:53 +08:00
fengmk2
d888ef3297 Release 1.0.3 2014-08-01 13:31:54 +08:00
fengmk2
ce4ec7e6e9 Merge pull request #397 from cnpm/list-users-packages
add maintaining packages in user page
2014-08-01 13:19:30 +08:00
dead_horse
fe319b06ba Release 1.0.2 2014-08-01 12:05:54 +08:00
dead_horse
d829600ed0 ~_~ fix response message 2014-08-01 12:05:25 +08:00
dead_horse
4dd59cb300 Release 1.0.1 2014-08-01 12:00:33 +08:00
dead_horse
5ff25474c0 Merge pull request #398 from cnpm/fix-auth
hot fix auth error
2014-08-01 11:58:54 +08:00
dead_horse
6d49a859c6 hot fix auth error 2014-08-01 11:58:23 +08:00
dead_horse
094178c3ca add maintaining packages in user page 2014-08-01 10:47:47 +08:00
fengmk2
63a57b906e Release 1.0.0 2014-08-01 09:26:04 +08:00
fengmk2
a09e6b142d Merge pull request #394 from cnpm/list-pravites
add private package list
2014-08-01 08:55:11 +08:00
dead_horse
bca9341e5d add private package list 2014-08-01 01:43:13 +08:00
fengmk2
71b0662d49 Release 0.9.2 2014-07-30 23:15:11 +08:00
fengmk2
b894c0fa2b hotfix save custom user bug 2014-07-30 23:14:53 +08:00
fengmk2
4127fa1f07 Release 0.9.1 2014-07-30 17:20:17 +08:00
dead_horse
5ee2a6e8fd Merge pull request #390 from cnpm/handle-user-service-auth-throw-error
Handle user service auth throw custom error message
2014-07-30 10:16:24 +08:00
fengmk2
8baa34145b Handle user service auth throw custom error message 2014-07-30 10:05:55 +08:00
fengmk2
4acb819356 Merge pull request #389 from cnpm/private-white-list
Private white list
2014-07-30 08:21:43 +08:00
dead_horse
2a84e39aee add test for config private packages 2014-07-30 00:52:44 +08:00
dead_horse
2019d72e36 add config.privatePackages 2014-07-29 23:55:42 +08:00
dead_horse
b9fa7e7f98 add more comments in config/index.js 2014-07-29 23:31:22 +08:00
fengmk2
694795e451 Release 0.9.0 2014-07-29 10:24:56 +08:00
dead_horse
fae67880a5 Merge pull request #388 from cnpm/user-service
Custom user service. fixed #385
2014-07-29 10:17:08 +08:00
fengmk2
0929748cbd listMaintainers only return name and email 2014-07-29 10:06:28 +08:00
fengmk2
a87945c6a0 remove sessions 2014-07-29 01:55:18 +08:00
fengmk2
d7345dcc76 scopes init mv to services/user.js 2014-07-29 01:51:32 +08:00
fengmk2
1a9ea8c6cf show user more profile 2014-07-29 01:42:02 +08:00
fengmk2
8f0ada7bf8 registry show user support custom user service 2014-07-29 00:54:18 +08:00
fengmk2
ddd9c9557d support custom user service for user auth 2014-07-29 00:11:34 +08:00
fengmk2
9b2a846865 remove session middleware 2014-07-28 09:17:42 +08:00
fengmk2
b1b238e20c add DefaultUserService 2014-07-28 09:17:42 +08:00
fengmk2
26031a49d2 Merge pull request #383 from cnpm/issue382-limit-scope
Issue382 limit scope
2014-07-28 09:15:39 +08:00
dead_horse
8bfc8ce71d check scopes in module.getAdapt 2014-07-27 23:32:58 +08:00
dead_horse
dffae785ca test public mode, fix some logic, close #382 2014-07-27 23:28:38 +08:00
dead_horse
078d7ddf3d fix sync scope test 2014-07-27 23:28:38 +08:00
dead_horse
8ec082db89 move scope.js into publishable.js, add forcePublishWithScope 2014-07-27 23:28:38 +08:00
dead_horse
040b727878 config.scopes not exist, means do not support scope 2014-07-27 23:28:38 +08:00
dead_horse
02e1ba63d6 fix scope test cases 2014-07-25 09:39:33 +08:00
dead_horse
a16883618e add test, fix middleware 2014-07-25 00:23:26 +08:00
dead_horse
7bd1c85d4e fix adapt scope 2014-07-25 00:12:01 +08:00
dead_horse
336e6f0da8 fix debug info 2014-07-24 23:55:07 +08:00
dead_horse
d43266670e add assert scope middleware 2014-07-24 23:54:53 +08:00
fengmk2
8fdbbf0b04 Release 0.8.7 2014-07-24 00:05:47 +08:00
fengmk2
b62a3b78fb fix unpublished info missing maintainers cause TypeError 2014-07-24 00:05:28 +08:00
fengmk2
d6cf093844 Release 0.8.6 2014-07-23 13:07:33 +08:00
fengmk2
081f0cb4f8 show unpublished info on web package page. fixes #381 2014-07-23 13:06:20 +08:00
fengmk2
0bf18639b6 Release 0.8.5 2014-07-22 23:38:58 +08:00
dead_horse
cbe7a165b0 Merge pull request #380 from cnpm/private-package-default-scoped
Only private package support default scoped. fixed #378
2014-07-22 23:14:31 +08:00
fengmk2
7cadfb4007 Only private package support default scoped. fixed #378 2014-07-22 21:55:45 +08:00
fengmk2
ec51fcf70c Release 0.8.4 2014-07-22 13:32:47 +08:00
fengmk2
565a7d3cfe Merge pull request #379 from cnpm/get-adapt
adapt default scpoe in /@:scope/:name/:version
2014-07-22 12:51:53 +08:00
dead_horse
4ac857fba2 adapt default scpoe in /@:scope/:name/:version 2014-07-22 11:42:18 +08:00
fengmk2
af7c952d2f Release 0.8.3 2014-07-22 10:14:12 +08:00
fengmk2
6705172d4b hot fix download 2014-07-22 10:13:55 +08:00
fengmk2
c0295ecc77 Release 0.8.2 2014-07-22 01:00:38 +08:00
fengmk2
aaf7f04d08 fix default scope detect 2014-07-22 01:00:22 +08:00
fengmk2
beceac10dc Release 0.8.1 2014-07-21 23:54:45 +08:00
dead_horse
fad58273b6 Merge pull request #377 from cnpm/default-scope
support default @org. close #376
2014-07-21 17:16:21 +08:00
fengmk2
e0361dc1f2 add more test cases 2014-07-21 16:18:28 +08:00
fengmk2
d8c337acc5 support default @org. close #376 2014-07-21 16:13:43 +08:00
dead_horse
9d7683bd61 hotfix redis init error 2014-07-21 14:14:52 +08:00
fengmk2
994b2e47aa Release 0.8.0 2014-07-21 13:58:24 +08:00
dead_horse
b14faad3fb Merge pull request #375 from cnpm/scope-package
support scoped package
2014-07-21 13:56:13 +08:00
fengmk2
9163f793a3 update readme show scoped packages 2014-07-21 13:53:33 +08:00
fengmk2
1c0d4fdcdc support "scoped" packages. close #352 2014-07-21 13:51:43 +08:00
dead_horse
20fce9f55f Merge pull request #373 from cnpm/stop-old-publish-flow
Stop old publish flow
2014-07-19 01:21:22 +08:00
fengmk2
6b73a7762f use safe jsonp 2014-07-19 01:16:30 +08:00
fengmk2
fd34e0512c Stop support old publish flow. fix #368 2014-07-19 01:11:33 +08:00
fengmk2
a84715167e Merge pull request #372 from cnpm/sql
update SQLs
2014-07-13 07:07:59 +08:00
dead_horse
db1cb0d0f8 update SQLs 2014-07-12 23:20:55 +08:00
dead_horse
dc137f0821 bump redis 2014-07-12 14:57:23 +08:00
fengmk2
76d270fc77 Merge pull request #371 from cnpm/update-logger
use sync_info and sync_error categories
2014-07-11 12:06:28 +08:00
dead_horse
b5190b56e8 use sync_info and sync_error categories 2014-07-11 10:03:41 +08:00
fengmk2
d2d238db59 add categories to loggers. fix #370 2014-07-11 09:07:48 +08:00
fengmk2
4c02d84fec fix get latest tag always not exists bug 2014-07-11 01:04:52 +08:00
dead_horse
74585f0b8a Merge pull request #369 from cnpm/publish-with-tag
support `npm publish --tag beta`. fix #366
2014-07-10 23:47:48 +08:00
fengmk2
a6ac632e73 support npm publish --tag beta. fix #366 2014-07-10 23:34:25 +08:00
fengmk2
0b59547ac1 Merge pull request #365 from cnpm/logger
use mini-logger and error-formater
2014-07-09 11:30:48 +08:00
dead_horse
b5929c94e0 use mini-logger and error-formater 2014-07-09 11:23:20 +08:00
fengmk2
4c96682603 Release 0.7.0 2014-07-07 12:11:12 +08:00
dead_horse
78af0fc15b Merge pull request #364 from cnpm/maintainer
Maintainer logic improve
2014-07-07 11:42:53 +08:00
fengmk2
ce381618a0 use module_maintainers on GET /pakcage/:name page 2014-07-07 10:25:06 +08:00
fengmk2
87486c484f use new module_maintainers on GET /:name 2014-07-07 10:23:53 +08:00
fengmk2
4608712e05 admin user should never publish to other user's packages. fix #363
but admin user need have the permission to delete other's packages.
2014-07-07 01:41:03 +08:00
fengmk2
3c6576bcab Add a new table for module-maintainers.
* fix #362 update maintianers change the lastmodified. remove version and change last modified.
* #363 change isMaintainer detect logic.
2014-07-07 00:56:44 +08:00
fengmk2
ba1986b931 gravatar use https 2014-07-02 00:09:56 +08:00
fengmk2
5efa508376 support https 2014-07-01 23:26:48 +08:00
dead_horse
38617d4572 bump dependencies 2014-06-30 17:31:04 +08:00
fengmk2
a9a04b370d Release 0.6.1 2014-06-18 12:27:47 +08:00
fengmk2
ec1d924dc1 hot fix removeTagsByNames() 2014-06-17 10:41:41 +08:00
fengmk2
234d1ec4d6 fix _rev not exists 2014-06-16 15:52:56 +08:00
dead_horse
b8f4958d8f Merge pull request #357 from cnpm/fix-sync-request
sync unpublished on GET /sync/:name
2014-06-16 15:23:22 +08:00
fengmk2
e56a21e890 sync unpublished on GET /sync/:name 2014-06-16 15:21:53 +08:00
fengmk2
e3434a58c5 Release 0.6.0 2014-06-16 15:06:17 +08:00
dead_horse
cd9d38aadb Merge pull request #356 from cnpm/unpublished
sync unpublished info. close #353
2014-06-16 13:50:05 +08:00
fengmk2
0ff22ccc60 sync unpublished info. close #353 2014-06-16 10:54:02 +08:00
dead_horse
e4cfb13383 Merge pull request #354 from cnpm/delete-not-exists-versions
Delete not exists versions on sync worker. #353
2014-06-16 09:39:58 +08:00
fengmk2
23ad3a29b4 Delete not exists versions on sync worker. #353 2014-06-15 00:57:18 +08:00
dead_horse
f11b4e9954 Release 0.5.3 2014-06-13 18:08:12 +08:00
dead_horse
6e775eed47 fix sync response 204 2014-06-13 18:05:09 +08:00
dead_horse
ca6841c042 add links in History.md 2014-06-08 00:59:54 +08:00
dead_horse
652b900fc4 bump koa 2014-06-08 00:59:22 +08:00
dead_horse
43379cc66d fix test-cov 2014-06-04 17:37:19 +08:00
dead_horse
8f9b3db029 bump koa and should 2014-06-04 17:29:52 +08:00
fengmk2
884b75f6a3 Release 0.5.2 2014-06-04 15:41:01 +08:00
dead_horse
a958ee40c4 Merge pull request #350 from cnpm/hotfix
sync hotfix
2014-06-04 15:39:05 +08:00
fengmk2
1a01909750 sync hotfix 2014-06-04 15:35:50 +08:00
dead_horse
6901a5c994 Merge pull request #349 from cnpm/dist-sync-phantomjs
sync phantomjs downloads pkg. close #348
2014-06-04 15:12:31 +08:00
fengmk2
33b13dc30e sync phantomjs downloads pkg. close #348 2014-06-04 14:35:20 +08:00
fengmk2
73d9d2c3d0 Merge pull request #347 from cnpm/issue346-restart-syncer
add restart, fixed #346
2014-06-04 12:39:34 +08:00
dead_horse
8b955adb42 add restart, fixed #346 2014-06-04 10:57:50 +08:00
116 changed files with 8584 additions and 1879 deletions

1
.gitignore vendored
View File

@@ -27,3 +27,4 @@ bin/mysql.js
bin/test.sql
coverage/
config/web_readme.md
.tmp/

View File

@@ -18,3 +18,5 @@ coverage/
.jshintignore
.DS_Store
config/web_readme.md
.dist/
config/config.js

View File

@@ -1,3 +1,5 @@
language: node_js
node_js:
- '0.11'
script: "make test-travis"
after_script: "npm install coveralls@2 && cat ./coverage/lcov.info | coveralls"

View File

@@ -1,8 +1,323 @@
1.7.3 / 2014-11-12
==================
* fix(sync): should not sync package when maintainers sort change
1.7.2 / 2014-10-31
==================
* feat(sync): add min sync interval time detect
1.7.1 / 2014-10-15
==================
* fix typo in sync popular, fix [#477](https://github.com/cnpm/cnpmjs.org/issues/477)
1.7.0 / 2014-10-15
==================
* Merge pull request [#475](https://github.com/cnpm/cnpmjs.org/issues/475) from KidkArolis/configurable-short-registry-url
* If sourceNpmRegistry is cnpm - use it in getShort
* Merge pull request [#472](https://github.com/cnpm/cnpmjs.org/issues/472) from cnpm/issue468-upon
* fix test label
* add sync popular modules, close [#468](https://github.com/cnpm/cnpmjs.org/issues/468)
* add sendmail test script
1.6.1 / 2014-10-09
==================
* make test on travis faster
* ensure not sync user also has his own package names
* add [v1.6.x-upgrade.sql](https://github.com/cnpm/cnpmjs.org/blob/master/docs/update_sqls/v1.6.x-upgrade.sql)
* save npm original package maintainers to npm_module_maintainer table. fixed [#464](https://github.com/cnpm/cnpmjs.org/issues/464)
* use simple 404
1.6.0 / 2014-10-08
==================
* list user all packages api. fixed [#462](https://github.com/cnpm/cnpmjs.org/issues/462)
* add node-dev: $ make dev
* always start sync worker
* update node mailer
* update autod
1.5.5 / 2014-09-25
==================
* fix sync in web
* sync upstream only the first package. make sync devDependencies optionsal, default is false
* add some comment, default sourceNpmRegistryIsCNpm to true
1.5.4 / 2014-09-24
==================
* format sync log
1.5.3 / 2014-09-24
==================
* support sync upstream first. fixed [#451](https://github.com/cnpm/cnpmjs.org/issues/451)
1.5.2 / 2014-09-24
==================
* support im url on user profile page; update bootstrap to 3.2.0
1.5.1 / 2014-09-23
==================
* search support case insensitive, close [#450](https://github.com/cnpm/cnpmjs.org/issues/450)
* add config._syncInWeb, close [#448](https://github.com/cnpm/cnpmjs.org/issues/448)
* show maintainers when publish 403. fixed [#430](https://github.com/cnpm/cnpmjs.org/issues/430)
* no attachment for html
1.5.0 / 2014-09-15
==================
* dist sync document too. fixed [#420](https://github.com/cnpm/cnpmjs.org/issues/420)
1.4.4 / 2014-09-12
==================
* badge version support 1.0.0-beta1. fixed [#440](https://github.com/cnpm/cnpmjs.org/issues/440)
1.4.3 / 2014-09-09
==================
* alias /:name/-/:file to /:name/download/:file. fixed [#439](https://github.com/cnpm/cnpmjs.org/issues/439)
1.4.2 / 2014-09-03
==================
* change default source registry to taobao's registry
* Merge pull request [#435](https://github.com/cnpm/cnpmjs.org/issues/435) from cnpm/bluebird
* add bluebird
* bump fs-cnpm
* Merge pull request [#434](https://github.com/cnpm/cnpmjs.org/issues/434) from cnpm/agent-stat
* show agent sockets stat. fixed [#433](https://github.com/cnpm/cnpmjs.org/issues/433)
* update readme
* remove pic in readme
1.4.1 / 2014-08-20
==================
* fix login error status
1.4.0 / 2014-08-20
==================
* different version, different color badge, add version badge. fixed [#427](https://github.com/cnpm/cnpmjs.org/issues/427)
* add download and node version badge
1.3.2 / 2014-08-18
==================
* remove unused eventproxy
* add custom config in tools/sync_not_exist.js
1.3.1 / 2014-08-18
==================
* add sync not exist tools, close [#424](https://github.com/cnpm/cnpmjs.org/issues/424)
* use gittip instand of alipay. close [#425](https://github.com/cnpm/cnpmjs.org/issues/425)
* update registry api doc
1.3.0 / 2014-08-11
==================
* ignore config/config.js
* Merge pull request [#421](https://github.com/cnpm/cnpmjs.org/issues/421) from cnpm/qn-cnpm
* fix test case
* use fs-cnpm
* fix test
* use qn-cnpm
* bump cfork
1.2.2 / 2014-08-08
==================
* bump koa
1.2.1 / 2014-08-07
==================
* deprecated bug fix and support undeprecate
1.2.0 / 2014-08-07
==================
* show deprecated message
* Sync deprecated field if it missing
* Support $ cnpm deprecate [pkgname]@[version] "message". fixed [#415](https://github.com/cnpm/cnpmjs.org/issues/415)
1.1.0 / 2014-08-07
==================
* Add user to maintainers when publish. fixed [#395](https://github.com/cnpm/cnpmjs.org/issues/395)
* List all npm registry api. close [#413](https://github.com/cnpm/cnpmjs.org/issues/413)
* limit list since
* change deps by "~"
* use cfork to make sure worker fork and restart
* handle master uncaughtException. fixed [#403](https://github.com/cnpm/cnpmjs.org/issues/403)
1.0.6 / 2014-08-02
==================
* WTF moment@2.8.0 missing
1.0.5 / 2014-08-02
==================
* unpublish pkg@version bug hotfix. fixed [#400](https://github.com/cnpm/cnpmjs.org/issues/400)
1.0.4 / 2014-08-01
==================
* hotfix [#399](https://github.com/cnpm/cnpmjs.org/issues/399) use not exists
1.0.3 / 2014-08-01
==================
* add maintaining packages in user page
1.0.2 / 2014-08-01
==================
* ~_~ fix auth error response message
1.0.1 / 2014-08-01
==================
* Merge pull request [#398](https://github.com/cnpm/cnpmjs.org/issues/398) from cnpm/fix-auth
* hot fix auth error
1.0.0 / 2014-08-01
==================
* add private package list
0.9.2 / 2014-07-30
==================
* hotfix save custom user bug
0.9.1 / 2014-07-30
==================
* Handle user service auth throw custom error message
* add test for config private packages
* add config.privatePackages
* add more comments in config/index.js
0.9.0 / 2014-07-29
==================
* scopes init mv to services/user.js
* show user more profile
* registry show user support custom user service
* support custom user service for user auth
* remove session middleware
* add DefaultUserService
* check scopes in module.getAdapt
* test public mode, fix some logic, close [#382](https://github.com/cnpm/cnpmjs.org/issues/382)
* move scope.js into publishable.js, add forcePublishWithScope
* config.scopes not exist, means do not support scope
* add assert scope middleware
0.8.7 / 2014-07-24
==================
* fix unpublished info missing maintainers cause TypeError
0.8.6 / 2014-07-23
==================
* show unpublished info on web package page. fixes [#381](https://github.com/cnpm/cnpmjs.org/issues/381)
0.8.5 / 2014-07-22
==================
* Only private package support default scoped. fixed [#378](https://github.com/cnpm/cnpmjs.org/issues/378)
0.8.4 / 2014-07-22
==================
* adapt default scpoe in /@:scope/:name/:version
0.8.3 / 2014-07-22
==================
* hot fix download
0.8.2 / 2014-07-22
==================
* fix default scope detect
0.8.1 / 2014-07-21
==================
* add more test cases
* support default @org. close [#376](https://github.com/cnpm/cnpmjs.org/issues/376)
* hotfix redis init error
0.8.0 / 2014-07-21
==================
* support "scoped" packages. close [#352](https://github.com/cnpm/cnpmjs.org/issues/352)
* use safe jsonp
* Stop support old publish flow. fix [#368](https://github.com/cnpm/cnpmjs.org/issues/368)
* update SQLs
* use sync_info and sync_error categories
* add categories to loggers. fix [#370](https://github.com/cnpm/cnpmjs.org/issues/370)
* fix get latest tag always not exists bug
* support `npm publish --tag beta`. fix [#366](https://github.com/cnpm/cnpmjs.org/issues/366)
* use mini-logger and error-formater
0.7.0 / 2014-07-07
==================
* use module_maintainers on GET /pakcage/:name page
* use new module_maintainers on GET /:name
* admin user should never publish to other user's packages. fix [#363](https://github.com/cnpm/cnpmjs.org/issues/363)
* Add a new table for module-maintainers.
* gravatar use https
* support https
0.6.1 / 2014-06-18
==================
* hot fix removeTagsByNames()
* fix _rev not exists
* sync unpublished on GET /sync/:name
0.6.0 / 2014-06-16
==================
* sync unpublished info. close [#353](https://github.com/cnpm/cnpmjs.org/issues/353)
* Delete not exists versions on sync worker. [#353](https://github.com/cnpm/cnpmjs.org/issues/353)
0.5.3 / 2014-06-13
==================
* fix sync response 204
* add links in History.md
* bump koa
* fix test-cov
* bump koa and should
0.5.2 / 2014-06-04
==================
* sync hotfix
* sync phantomjs downloads pkg. close [#348](https://github.com/cnpm/cnpmjs.org/issues/348)
* add restart, fixed [#346](https://github.com/cnpm/cnpmjs.org/issues/346)
0.5.1 / 2014-05-28
==================
* fix attack on /-/all/since?stale=update_after&startkey=2 close #336
* fix attack on /-/all/since?stale=update_after&startkey=2 close [#336](https://github.com/cnpm/cnpmjs.org/issues/336)
* bump thunkify-wrap
* bump koa-middlewares
* remove outputError
@@ -21,28 +336,28 @@
* add sync dist to sync/index.js
* show dist page
* sync dist file and save it to database
* disable gzip before #335 has fix
* disable gzip before [#335](https://github.com/cnpm/cnpmjs.org/issues/335) has fix
0.4.3 / 2014-04-18
0.4.3 / 2014-04-18
==================
* Merge pull request #334 from cnpm/fix-permission
* Merge pull request [#334](https://github.com/cnpm/cnpmjs.org/issues/334) from cnpm/fix-permission
* add permission check to /:name/:tag
* Merge pull request #333 from cnpm/issue332-tag
* Merge pull request [#333](https://github.com/cnpm/cnpmjs.org/issues/333) from cnpm/issue332-tag
* fix space
* add put /:name/:tag, close #332
* add put /:name/:tag, close [#332](https://github.com/cnpm/cnpmjs.org/issues/332)
0.4.2 / 2014-04-17
==================
* sync interval config
* fix fav ico and show pkg size on pkg info page. fix #318
* sync work sync one done must wait for a defer.setImmediate. fix #328
* fix fav ico and show pkg size on pkg info page. fix [#318](https://github.com/cnpm/cnpmjs.org/issues/318)
* sync work sync one done must wait for a defer.setImmediate. fix [#328](https://github.com/cnpm/cnpmjs.org/issues/328)
* bump dep versions
* if download tarball 404, throw err better than ignore it. fixed #325
* if download tarball 404, throw err better than ignore it. fixed [#325](https://github.com/cnpm/cnpmjs.org/issues/325)
* refator sync
* hotfix, close #321
* hotfix, close #319
* hotfix, close [#321](https://github.com/cnpm/cnpmjs.org/issues/321)
* hotfix, close [#319](https://github.com/cnpm/cnpmjs.org/issues/319)
* support custom web home page
* npm get short only can read from cnpm now
* if using reverted proxy like nginx, only binding on local host
@@ -58,18 +373,18 @@
* fix test cases to run on local machine
* add contribute guidelines
* use local mysql for dev env. fix #308
* use local mysql for dev env. fix [#308](https://github.com/cnpm/cnpmjs.org/issues/308)
* use copy to
* use koa-compress and koa-conditional-get
* maintainers is string, fix #301
* maintainers is string, fix [#301](https://github.com/cnpm/cnpmjs.org/issues/301)
0.3.13 / 2014-03-27
==================
* fix npm adduser update 409 bug
* fix multiline coverage
* show package engines. fixed #280
* dont sync local package field. fix #295
* show package engines. fixed [#280](https://github.com/cnpm/cnpmjs.org/issues/280)
* dont sync local package field. fix [#295](https://github.com/cnpm/cnpmjs.org/issues/295)
0.3.12 / 2014-03-26
==================
@@ -77,53 +392,53 @@
* fix result.successes not exist error
* fix search list
* add simple request for listall
* only return package name in /-/all and /-/all/since, fixed #291
* only return package name in /-/all and /-/all/since, fixed [#291](https://github.com/cnpm/cnpmjs.org/issues/291)
* refine docs foloder
* use module gmt_modified as etag. fix #288
* use module gmt_modified as etag. fix [#288](https://github.com/cnpm/cnpmjs.org/issues/288)
* fix typo, remove unused config in package.json
* web page only list cnpm registry related info
* use generator in qnfs
0.3.11 / 2014-03-20
0.3.11 / 2014-03-20
==================
* use common.isMaintainer, fixed #283
* use common.isMaintainer, fixed [#283](https://github.com/cnpm/cnpmjs.org/issues/283)
* update dependencies
* use co-mocha for test, fixed #279
* use co-mocha for test, fixed [#279](https://github.com/cnpm/cnpmjs.org/issues/279)
* update thunkify-wrap, breaking change in thunkify-wrap
* refactor SQLs by using multiline
* use multiline to refactor sqls
* ignore contributors
0.3.10 / 2014-03-16
0.3.10 / 2014-03-16
==================
* Only /_session request send the authSession. fixed #223
* sync npm user info when maintainers and contributors not exists. fixed #82
* Only /_session request send the authSession. fixed [#223](https://github.com/cnpm/cnpmjs.org/issues/223)
* sync npm user info when maintainers and contributors not exists. fixed [#82](https://github.com/cnpm/cnpmjs.org/issues/82)
* save npm user to mysql
* password salt always be randoms
* remove session access in /name and /name/version, fixed #274
* remove session access in /name and /name/version, fixed [#274](https://github.com/cnpm/cnpmjs.org/issues/274)
* fix update maintainer session error
* update koa-middlewares
* fix test, fix sync_by_install
* use defer session
* Support npm owner|author add [name] [pkg]. fixed #271
* Support npm owner|author add [name] [pkg]. fixed [#271](https://github.com/cnpm/cnpmjs.org/issues/271)
0.3.9 / 2014-03-14
0.3.9 / 2014-03-14
==================
* custom user-agent
* use co-urllib instead of thunkify urllib; fix mock http.request test cases
* request limit custom message
* add config.redis check
* add koa-limit, fixed #267
* add koa-limit, fixed [#267](https://github.com/cnpm/cnpmjs.org/issues/267)
0.3.8 / 2014-03-11
0.3.8 / 2014-03-11
==================
* update middlewares, fixed missing charset bug #264
* update middlewares, fixed missing charset bug [#264](https://github.com/cnpm/cnpmjs.org/issues/264)
0.3.7 / 2014-03-11
0.3.7 / 2014-03-11
==================
* show worker die date time
@@ -133,63 +448,63 @@
* fix return versions
* fix makefile, remove eventproxy
* refactor sync_module_worker
* add make test-dev, fixed #259
* add make test-dev, fixed [#259](https://github.com/cnpm/cnpmjs.org/issues/259)
* change npm.js to generator
* update urllib, proxy/npm.js use generator
* sync_all and sync_exist to generator
* change function to generator
* need node >= v0.11.9
0.3.6 / 2014-03-06
0.3.6 / 2014-03-06
==================
* install missing package should sync it from source npm. fixed #252
* install missing package should sync it from source npm. fixed [#252](https://github.com/cnpm/cnpmjs.org/issues/252)
* npm publish dont contains .jshint*
* npm test run jshint
* Add jshint check: $ make jshint
* use `yield* next` instead of `yield next`
* replace dist.u.qiniudn.com with cnpmjs.org/dist
0.3.5 / 2014-03-05
0.3.5 / 2014-03-05
==================
* redirect /dist/xxx.tgz => http://dist.u.qiniudn.com/xxx.tgz fixed #249
* redirect /name to /package/name when /name is 404. fixed #245
* Add missing properies and sync missing star users. fixed #235
* redirect /dist/xxx.tgz => http://dist.u.qiniudn.com/xxx.tgz fixed [#249](https://github.com/cnpm/cnpmjs.org/issues/249)
* redirect /name to /package/name when /name is 404. fixed [#245](https://github.com/cnpm/cnpmjs.org/issues/245)
* Add missing properies and sync missing star users. fixed [#235](https://github.com/cnpm/cnpmjs.org/issues/235)
0.3.4 / 2014-03-04
0.3.4 / 2014-03-04
==================
* add cov
* use istanbul run test coverage
* gzip support. fix #241
* gzip support. fix [#241](https://github.com/cnpm/cnpmjs.org/issues/241)
* readme spelling patch (@stanzheng)
* default readme to null, fixed #233
* default readme to null, fixed [#233](https://github.com/cnpm/cnpmjs.org/issues/233)
* remove readme in versions
0.3.3 / 2014-02-28
0.3.3 / 2014-02-28
==================
* Merge pull request #232 from cnpm/host-hotfix
* Merge pull request [#232](https://github.com/cnpm/cnpmjs.org/issues/232) from cnpm/host-hotfix
* get request host from request.headers
* Merge pull request #231 from cnpm/bug-fix
* fix deps display bug#230 and nsf.url TypeError#229
* Merge pull request [#231](https://github.com/cnpm/cnpmjs.org/issues/231) from cnpm/bug-fix
* fix deps display bug[#230](https://github.com/cnpm/cnpmjs.org/issues/230) and nsf.url TypeError[#229](https://github.com/cnpm/cnpmjs.org/issues/229)
0.3.2 / 2014-02-28
0.3.2 / 2014-02-28
==================
* update koa-sess and koa-redis
* fix sync all test
* remove nfs.downloadStream first, fix tmppath error
* fix fengmk2/giturl#1 bug
* fix fengmk2/giturl[#1](https://github.com/cnpm/cnpmjs.org/issues/1) bug
0.3.1 / 2014-02-27
0.3.1 / 2014-02-27
==================
* add etag fixed #224
* add etag fixed [#224](https://github.com/cnpm/cnpmjs.org/issues/224)
* travis ci install on source npm
0.3.0 / 2014-02-27
0.3.0 / 2014-02-27
==================
* fix typo and dont sync not exists pkgs
@@ -210,162 +525,162 @@
* fix all the test of registry module.test.js
* convert registry/module.js to koa type
* fix auth middleware
* finish registry user controller koa and update mm to support thunkify. fixed #196
* finish registry user controller koa and update mm to support thunkify. fixed [#196](https://github.com/cnpm/cnpmjs.org/issues/196)
* change controllers/user.js to koa
* thunkify all proxy
* convert all middlewares to koa type
* change regsitry sync to koa
* addd koa-jsonp, koa-bodyparser, fix / controller
* first koa run registry home page /
* Merge pull request #212 from cnpm/fix-sync-404
* Merge pull request [#212](https://github.com/cnpm/cnpmjs.org/issues/212) from cnpm/fix-sync-404
* return friendly 404 reason
* Merge pull request #211 from cnpm/bug-fix
* override json limit to default 10mb. fixed #209
* fix #210 addPackageAndDist package version detect bug
* Merge pull request [#211](https://github.com/cnpm/cnpmjs.org/issues/211) from cnpm/bug-fix
* override json limit to default 10mb. fixed [#209](https://github.com/cnpm/cnpmjs.org/issues/209)
* fix [#210](https://github.com/cnpm/cnpmjs.org/issues/210) addPackageAndDist package version detect bug
0.2.27 / 2014-02-19
0.2.27 / 2014-02-19
==================
* support json result in search, fixed #189
* support json result in search, fixed [#189](https://github.com/cnpm/cnpmjs.org/issues/189)
0.2.26 / 2014-02-19
0.2.26 / 2014-02-19
==================
* npm publish also need to add deps
0.2.25 / 2014-02-19
0.2.25 / 2014-02-19
==================
* max handle number of package.json `dependencies` property
* Dependents support. fixed #190
* Dependents support. fixed [#190](https://github.com/cnpm/cnpmjs.org/issues/190)
0.2.24 / 2014-02-13
0.2.24 / 2014-02-13
==================
* fix if delete all the versions
* refactor remove module, fixed #186
* refactor remove module, fixed [#186](https://github.com/cnpm/cnpmjs.org/issues/186)
0.2.23 / 2014-01-26
0.2.23 / 2014-01-26
==================
* system admin can add, publish, remove the packages. fixed #176
* system admin can add, publish, remove the packages. fixed [#176](https://github.com/cnpm/cnpmjs.org/issues/176)
0.2.22 / 2014-01-26
0.2.22 / 2014-01-26
==================
* add keyword and search support keyword. #181
* add keyword and search support keyword. [#181](https://github.com/cnpm/cnpmjs.org/issues/181)
0.2.21 / 2014-01-24
0.2.21 / 2014-01-24
==================
* refactor code styles on package.html
* nav-tabs e.preventDefault
* Show registry server error response. fixed #178
* Show registry server error response. fixed [#178](https://github.com/cnpm/cnpmjs.org/issues/178)
* nav-tabs for package.html (@4simple)
0.2.20 / 2014-01-23
0.2.20 / 2014-01-23
==================
* hotfix sync missing dependencies and readmes
* fix sync readme error, fixed #174
* fix sync readme error, fixed [#174](https://github.com/cnpm/cnpmjs.org/issues/174)
* add updateReadme in module
0.2.19 / 2014-01-22
0.2.19 / 2014-01-22
==================
* npm install no need to check authorization header. fixed #171
* npm install no need to check authorization header. fixed [#171](https://github.com/cnpm/cnpmjs.org/issues/171)
0.2.18 / 2014-01-20
0.2.18 / 2014-01-20
==================
* Support gitlab git url to display and click. fixed #160
* Support gitlab git url to display and click. fixed [#160](https://github.com/cnpm/cnpmjs.org/issues/160)
* fix redis crash
0.2.17 / 2014-01-17
0.2.17 / 2014-01-17
==================
* custom logo url
* hotfix layout bug
0.2.16 / 2014-01-16
0.2.16 / 2014-01-16
==================
* fix publish-time bug
0.2.15 / 2014-01-16
0.2.15 / 2014-01-16
==================
* add publish_time to debug
0.2.14 / 2014-01-16
0.2.14 / 2014-01-16
==================
* add make autod
* update publish_time, fixed #163
* update publish_time, fixed [#163](https://github.com/cnpm/cnpmjs.org/issues/163)
0.2.13 / 2014-01-15
0.2.13 / 2014-01-15
==================
* markdown tmpl not support footer, need to wrap on app start
0.2.12 / 2014-01-15
0.2.12 / 2014-01-15
==================
* add footer and npm client name customable
0.2.11 / 2014-01-15
0.2.11 / 2014-01-15
==================
* package page contributor link to search, default is true
0.2.10 / 2014-01-14
0.2.10 / 2014-01-14
==================
* fix #155 Content-Disposition wrong.
* fix [#155](https://github.com/cnpm/cnpmjs.org/issues/155) Content-Disposition wrong.
0.2.9 / 2014-01-14
0.2.9 / 2014-01-14
==================
* support startkey=c and startkey="c"
* support couch db search api. fixed #153
* support couch db search api. fixed [#153](https://github.com/cnpm/cnpmjs.org/issues/153)
* fix fork me image link
* support sync by query.name
0.2.8 / 2014-01-14
0.2.8 / 2014-01-14
==================
* dont show err stack on test env
* add download link for package page
0.2.7 / 2014-01-13
0.2.7 / 2014-01-13
==================
* add shasum when nfs.upload and hfs.uploadBuffer, fixed #148
* add shasum when nfs.upload and hfs.uploadBuffer, fixed [#148](https://github.com/cnpm/cnpmjs.org/issues/148)
0.2.6 / 2014-01-13
0.2.6 / 2014-01-13
==================
* support custom session store, fixed #146
* support custom session store, fixed [#146](https://github.com/cnpm/cnpmjs.org/issues/146)
0.2.5 / 2014-01-13
0.2.5 / 2014-01-13
==================
* add download timeout and unit test
* use downloadStream() first
* nfs download to a writeable stream.
0.2.4 / 2014-01-10
0.2.4 / 2014-01-10
==================
* set main script to index.js, fixed #142
* set main script to index.js, fixed [#142](https://github.com/cnpm/cnpmjs.org/issues/142)
0.2.3 / 2014-01-10
0.2.3 / 2014-01-10
==================
* Dont show sync button on private package
* Sync package as publish with no deps. fixed #138
* Sync package as publish with no deps. fixed [#138](https://github.com/cnpm/cnpmjs.org/issues/138)
0.2.2 / 2014-01-10
0.2.2 / 2014-01-10
==================
* keep compatibility
@@ -375,23 +690,23 @@
* new npm publish in one request, add _publish_in_cnpm
* support unsure name ufs
* contributors maybe a object
* Object #<Object> has no method 'forEach' fixed #134
* support custom config as a module, fixed issue #132
* support npm new publish flow. fixed #129
* Object #<Object> has no method 'forEach' fixed [#134](https://github.com/cnpm/cnpmjs.org/issues/134)
* support custom config as a module, fixed issue [#132](https://github.com/cnpm/cnpmjs.org/issues/132)
* support npm new publish flow. fixed [#129](https://github.com/cnpm/cnpmjs.org/issues/129)
* add toString and constructor to test admin
* fix #119 hasOwnProperty check admin bug.
* fix [#119](https://github.com/cnpm/cnpmjs.org/issues/119) hasOwnProperty check admin bug.
0.2.0 / 2013-12-27
0.2.0 / 2013-12-27
==================
* remove to lower case
* fix #127 execSync and execsync.
* fix [#127](https://github.com/cnpm/cnpmjs.org/issues/127) execSync and execsync.
* add contributors list on package page
* mv blanket to config
* sync typeerror fix #statusCode
* add disturl
* fix #122 admin security bug
* fixed #121, let pkg 404 as success
* fix [#122](https://github.com/cnpm/cnpmjs.org/issues/122) admin security bug
* fixed [#121](https://github.com/cnpm/cnpmjs.org/issues/121), let pkg 404 as success
* fix sql insert error
* fix typos
@@ -403,78 +718,78 @@
* make sure all packages name are lower case
* select ids from tag
* fix nodejsctl
* fix #112 missing versions and time no sync
* fix [#112](https://github.com/cnpm/cnpmjs.org/issues/112) missing versions and time no sync
* remove restart command
* fix sync missing packages error
* fix web/readme.md, add install
* fix #109 pkg no times and no versions bug.
* fix [#109](https://github.com/cnpm/cnpmjs.org/issues/109) pkg no times and no versions bug.
0.1.2 / 2013-12-19
==================
* fix times not exists canot sync bug. fixed #101
* fix times not exists canot sync bug. fixed [#101](https://github.com/cnpm/cnpmjs.org/issues/101)
* support npm run command
* remove before_install and install in travis, fixed #102
* split all sub queries, fixed #104
* fix doc, fixed #103
* remove before_install and install in travis, fixed [#102](https://github.com/cnpm/cnpmjs.org/issues/102)
* split all sub queries, fixed [#104](https://github.com/cnpm/cnpmjs.org/issues/104)
* fix doc, fixed [#103](https://github.com/cnpm/cnpmjs.org/issues/103)
* fix search too slow.
* dont email sync log level info
* only sync missing packages at first time
* update dependencies
* sync all will sync all the missing packages, fixed #97
* sync all will sync all the missing packages, fixed [#97](https://github.com/cnpm/cnpmjs.org/issues/97)
0.1.0 / 2013-12-12
==================
* add sync title
* add favicon. fixed #69
* refine sync page, fiexd #70
* add favicon. fixed [#69](https://github.com/cnpm/cnpmjs.org/issues/69)
* refine sync page, fiexd [#70](https://github.com/cnpm/cnpmjs.org/issues/70)
* add app version
* add test for sync
* refine sync page
* registry and web all use controllers/sync.js
* sync from web, fixed #58
* sync from web, fixed [#58](https://github.com/cnpm/cnpmjs.org/issues/58)
* saving missing descriptions
* add package download info. fixed #63
* add package download info. fixed [#63](https://github.com/cnpm/cnpmjs.org/issues/63)
* add avatar
* use dependecies, fixed #issue62
* support open search, fixed #60
* make sure publish_time and author is same to source npm registry. fixed #56
* support open search, fixed [#60](https://github.com/cnpm/cnpmjs.org/issues/60)
* make sure publish_time and author is same to source npm registry. fixed [#56](https://github.com/cnpm/cnpmjs.org/issues/56)
* add test for search
* add a simple search by mysql like
* fix This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery. fixed #54
* fix This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery. fixed [#54](https://github.com/cnpm/cnpmjs.org/issues/54)
* update install doc, use nodejsctl to start
* must add limit on list by author sql
* fix sql, change test to fit my local database, fixed #46
* fix sql, change test to fit my local database, fixed [#46](https://github.com/cnpm/cnpmjs.org/issues/46)
* use registry.cnpmjs.org
* add install document and total package info on home page. fix #42
* add module_id to tag table. #46
* skip error version. fixed #43
* add install document and total package info on home page. fix [#42](https://github.com/cnpm/cnpmjs.org/issues/42)
* add module_id to tag table. [#46](https://github.com/cnpm/cnpmjs.org/issues/46)
* skip error version. fixed [#43](https://github.com/cnpm/cnpmjs.org/issues/43)
* sync may make a user do not exist in database, but have modules in registry
* add user page
* fix set license
* ignore 404 on sync. fixed #39
* ignore 404 on sync. fixed [#39](https://github.com/cnpm/cnpmjs.org/issues/39)
* fix module page, add test
* update urllib to 0.5.5
* version and tag
* add module page
* fix download url
* first get tag, then try version
* support sync triggle by install, finish #31
* support sync triggle by install, finish [#31](https://github.com/cnpm/cnpmjs.org/issues/31)
* addTag error return 500
* just one download field
* add download total info on home page
* add download count
* versions empty and also check missing tags
* remove tags on unpublish
* add module tag. fix #6
* add module tag. fix [#6](https://github.com/cnpm/cnpmjs.org/issues/6)
* add [done] flag to check sync done on client
* get sync log #29
* get sync log [#29](https://github.com/cnpm/cnpmjs.org/issues/29)
* fix test in module
* rm tmp file on down request error
* add time for debug str
* fix pkg not exists null bug
* use sync module woker to handle sync process. fixed #19
* use sync module woker to handle sync process. fixed [#19](https://github.com/cnpm/cnpmjs.org/issues/19)
* if private mode enable, only admin can publish module
* add alias in readme
* fix sql, add sort by name
@@ -483,15 +798,15 @@
* add npm and cnpm image
* add registry total info on home page
* fix mods bug in module.removeAll, change module.update => module.removeWithVersions
* add test, fix bug. fixed #18
* add test, fix bug. fixed [#18](https://github.com/cnpm/cnpmjs.org/issues/18)
* spoort unpublish
* add web page index readme
* switchable nfs #21
* switchable nfs [#21](https://github.com/cnpm/cnpmjs.org/issues/21)
* change file path to match npm file path
* use qn cdn to store tarball file fixed #16
* add GET /:name/:version, fixed #3
* use qn cdn to store tarball file fixed [#16](https://github.com/cnpm/cnpmjs.org/issues/16)
* add GET /:name/:version, fixed [#3](https://github.com/cnpm/cnpmjs.org/issues/3)
* add module controller test cases; fix next module not exists logic bug.
* publish module flow finish #11
* publish module flow finish [#11](https://github.com/cnpm/cnpmjs.org/issues/11)
* add test for controllers/registry/user.js
* add test for middleware/auth
* add test for proxy/user
@@ -502,9 +817,9 @@
* add start time
* add home page
* remove session controller
* adduser() finish fixed #5
* adduser() finish fixed [#5](https://github.com/cnpm/cnpmjs.org/issues/5)
* rm app.js and routes.js
* Mock npm adduser server response, fixing #5
* Mock npm adduser server response, fixing [#5](https://github.com/cnpm/cnpmjs.org/issues/5)
* adjust project dir, separate registry and web server
* Init rest frame for cnpmjs.org
* init

View File

@@ -1,12 +1,12 @@
TESTS = $(shell ls -S `find test -type f -name "*.test.js" -print`)
REPORTER = tap
REPORTER = spec
TIMEOUT = 30000
MOCHA_OPTS =
REGISTRY = --registry=http://registry.npm.taobao.org
REGISTRY = --registry=https://registry.npm.taobao.org
install:
@npm install $(REGISTRY) \
--disturl=http://dist.cnpmjs.org
--disturl=https://npm.taobao.org/dist
jshint: install
@-./node_modules/.bin/jshint ./
@@ -16,19 +16,21 @@ pretest:
@mysql -uroot -e 'CREATE DATABASE cnpmjs_test;'
@mysql -uroot 'cnpmjs_test' < ./docs/db.sql
@mysql -uroot 'cnpmjs_test' -e 'show tables;'
@rm -rf .tmp/dist
test: install pretest
@NODE_ENV=test ./node_modules/.bin/mocha \
--harmony-generators \
--harmony \
--reporter $(REPORTER) \
--timeout $(TIMEOUT) \
--require should \
--require should-http \
--require co-mocha \
--require ./test/init.js \
$(MOCHA_OPTS) \
$(TESTS)
test-cov cov: install
test-cov cov: install pretest
@NODE_ENV=test node --harmony \
node_modules/.bin/istanbul cover --preserve-comments \
./node_modules/.bin/_mocha \
@@ -36,17 +38,40 @@ test-cov cov: install
--reporter $(REPORTER) \
--timeout $(TIMEOUT) \
--require should \
--require should-http \
--require co-mocha \
--require ./test/init.js \
$(MOCHA_OPTS) \
$(TESTS)
@./node_modules/.bin/cov coverage
test-travis: install pretest
@NODE_ENV=test CNPM_SOURCE_NPM=https://registry.npmjs.org CNPM_SOURCE_NPM_ISCNPM=false \
node --harmony \
node_modules/.bin/istanbul cover --preserve-comments \
./node_modules/.bin/_mocha \
--report lcovonly \
-- \
--reporter dot \
--timeout $(TIMEOUT) \
--require should \
--require should-http \
--require co-mocha \
--require ./test/init.js \
$(MOCHA_OPTS) \
$(TESTS)
dev:
@node_modules/.bin/node-dev --harmony dispatch.js
contributors: install
@./node_modules/.bin/contributors -f plain -o AUTHORS
autod: install
@./node_modules/.bin/autod -w -e public,view,docs,backup,coverage
@./node_modules/.bin/autod -w \
--prefix "~"\
--exclude public,view,docs,backup,coverage \
--dep bluebird \
--devdep mocha,should,istanbul-harmony,jshint
@$(MAKE) install
.PHONY: test

View File

@@ -1,9 +1,31 @@
cnpmjs.org
=======
[![Build Status](https://secure.travis-ci.org/cnpm/cnpmjs.org.svg)](http://travis-ci.org/cnpm/cnpmjs.org) [![Dependency Status](https://gemnasium.com/cnpm/cnpmjs.org.svg)](https://gemnasium.com/cnpm/cnpmjs.org)
[![NPM version][npm-image]][npm-url]
[![build status][travis-image]][travis-url]
[![Test coverage][coveralls-image]][coveralls-url]
[![Gittip][gittip-image]][gittip-url]
[![David deps][david-image]][david-url]
[![node version][node-image]][node-url]
[![npm download][download-image]][download-url]
[![gitter][gitter-image]][gitter-url]
[![NPM](https://nodei.co/npm/cnpmjs.org.svg?downloads=true&stars=true)](https://nodei.co/npm/cnpmjs.org/)
[npm-image]: http://cnpmjs.org/badge/v/cnpmjs.org.svg?style=flat-square
[npm-url]: http://cnpmjs.org/package/cnpmjs.org
[travis-image]: https://img.shields.io/travis/cnpm/cnpmjs.org.svg?style=flat-square
[travis-url]: https://travis-ci.org/cnpm/cnpmjs.org
[coveralls-image]: https://img.shields.io/coveralls/cnpm/cnpmjs.org.svg?style=flat-square
[coveralls-url]: https://coveralls.io/r/cnpm/cnpmjs.org?branch=master
[gittip-image]: https://img.shields.io/gittip/fengmk2.svg?style=flat-square
[gittip-url]: https://www.gittip.com/fengmk2/
[david-image]: https://img.shields.io/david/cnpm/cnpmjs.org.svg?style=flat-square
[david-url]: https://david-dm.org/cnpm/cnpmjs.org
[node-image]: https://img.shields.io/badge/node.js-%3E=_0.11-red.svg?style=flat-square
[node-url]: http://nodejs.org/download/
[download-image]: https://img.shields.io/npm/dm/cnpmjs.org.svg?style=flat-square
[download-url]: https://npmjs.org/package/cnpmjs.org
[gitter-image]: https://badges.gitter.im/Join%20Chat.svg
[gitter-url]: https://gitter.im/cnpm/cnpmjs.org?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
![logo](https://raw.github.com/cnpm/cnpmjs.org/master/logo.png)
@@ -22,6 +44,7 @@ Our goal is to provide a low cost maintenance and easy to use solution for priva
### Features
* **Support "scoped" packages**: [npm/npm#5239](https://github.com/npm/npm/issues/5239)
* **Simple to deploy**: only need `mysql` and a [simple store system](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide).
You can get the source code through `npm` or `git`.
* **Low cost and easy maintenance**: `package.json` info store in MySQL, tarball(tgz file) store in CDN or other store systems.
@@ -34,6 +57,7 @@ to extend `npm` with more features(`sync` command, [gzip](https://github.com/npm
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`,
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)
## Getting Start
@@ -42,15 +66,13 @@ only need to change the registry in config. Even include manual synchronization
* Mirror NPM in China: [cnpmjs.org](http://cnpmjs.org)
* 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)
* [NFS guide](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide)
![cnpm](https://docs.google.com/drawings/d/12QeQfGalqjsB77mRnf5Iq5oSXHCIUTvZTwECMonqCmw/pub?w=480&h=360)
* [wiki](https://github.com/cnpm/cnpmjs.org/wiki)
## Develop on your local machine
### Dependencies
* [node](http://nodejs.org) >=0.11.9
* [node](http://nodejs.org) >=0.11.12, use `--harmony`
* [mysql](http://dev.mysql.com/downloads/) >= 0.5.0, include `mysqld` and `mysql cli`. I test on `mysql@5.6.16`.
### Start MySQL
@@ -77,8 +99,8 @@ $ make test-cov
# udpate dependencies
$ make autod
# start server
$ node --harmony_generators dispatch.js
# start server with development mode
$ make dev
```
## How to contribute
@@ -90,24 +112,6 @@ $ node --harmony_generators dispatch.js
Tips: make sure your code is following the [node-style-guide](https://github.com/felixge/node-style-guide).
## Authors
```bash
$ git summary
project : cnpmjs.org
repo age : 4 months ago
commits : 472
active : 167 days
files : 104
authors :
272 fengmk2 57.6%
195 dead_horse 41.3%
2 4simple 0.4%
2 Stanley Zheng 0.4%
1 Alsotang 0.2%
```
## License
(The MIT License)

View File

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

View File

@@ -15,73 +15,48 @@
* Module dependencies.
*/
var util = require('util');
var moment = require('moment');
var logstream = require('logfilestream');
var ms = require('ms');
var formater = require('error-formater');
var Logger = require('mini-logger');
var config = require('../config');
var utility = require('utility');
var mail = require('./mail');
var util = require('util');
var isTEST = process.env.NODE_ENV === 'test';
var ONE_DAY = ms('1d');
var levels = ['info', 'warn', 'error'];
var categories = ['sync_info', 'sync_error'];
levels.forEach(function (catetory) {
var options = {
logdir: config.logdir,
duration: ONE_DAY,
nameformat: '[' + catetory + '.]YYYY-MM-DD[.log]'
};
var stream = logstream(options);
function write(msg) {
var time = moment().format('YYYY-MM-DD HH:mm:ss.SSS');
var subject = null;
if (msg instanceof Error) {
subject = msg.name;
var err = {
name: msg.name,
code: msg.code,
message: msg.message,
stack: msg.stack,
host: msg.host,
url: msg.url,
data: msg.data
};
if (err.name === 'Error' && typeof err.code === 'string') {
err.name = err.code + err.name;
}
err.name += 'Exception';
if (err.host) {
err.message += ' (' + err.host + ')';
}
msg = util.format('%s nodejs.%s: %s\nURL: %s\nData: %j\n%s\n\n',
time,
err.name,
err.stack,
err.url,
err.data,
time
);
} else {
msg = time + ' ' + util.format.apply(util, arguments) + '\n';
}
if (!isTEST) {
stream.write(msg);
if (config.debug) {
var level = catetory;
console.log('[' + level + '] ' + msg);
} else {
if (catetory === 'error' && subject) {
// send error email
var to = [];
for (var name in config.admins) {
to.push(config.admins[name]);
}
mail.error(to, subject, msg);
}
}
}
}
exports[catetory] = write;
var logger = module.exports = Logger({
categories: categories,
dir: config.logdir,
duration: '1d',
format: '[{category}.]YYYY-MM-DD[.log]',
stdout: config.debug && !isTEST,
errorFormater: errorFormater
});
var to = [];
for (var user in config.admins) {
to.push(config.admins[user]);
}
function errorFormater(err) {
var msg = formater.both(err);
mail.error(to, msg.json.name, msg.text);
return msg.text;
}
logger.syncInfo = function () {
var args = [].slice.call(arguments);
if (typeof args[0] === 'string') {
args[0] = util.format('[%s][%s] ', utility.logDate(), process.pid) + args[0];
}
logger.sync_info.apply(logger, args);
};
logger.syncError =function () {
var args = [].slice.call(arguments);
if (typeof args[0] === 'string') {
args[0] = util.format('[%s][%s] ', utility.logDate(), process.pid) + args[0];
}
logger.sync_error.apply(logger, arguments);
};

View File

@@ -14,10 +14,30 @@
* Module dependencies.
*/
var utility = require('utility');
var nodemailer = require('nodemailer');
var os = require('os');
var mailConfig = require('../config').mail;
var nodemailer = require('nodemailer');
var utility = require('utility');
var os = require('os');
var smtpConfig;
if (mailConfig.auth) {
// new style
smtpConfig = mailConfig;
} else {
smtpConfig = {
// backward compat
host: mailConfig.host,
port: mailConfig.port,
secure: mailConfig.secure || mailConfig.ssl,
debug: mailConfig.debug,
auth: {
user: mailConfig.user,
pass: mailConfig.pass
}
};
}
var transport = nodemailer.createTransport(smtpConfig);
/**
* Send notice email with mail level and appname.
@@ -51,26 +71,14 @@ LEVELS.forEach(function (level) {
exports.send = function (to, subject, html, callback) {
callback = callback || utility.noop;
var transport = nodemailer.createTransport("SMTP", {
host: mailConfig.host,
port: mailConfig.port,
secureConnection: mailConfig.ssl,
debug: mailConfig.debug,
auth: {
user: mailConfig.user,
pass: mailConfig.pass,
}
});
var message = {
sender: mailConfig.sender,
from: mailConfig.from || mailConfig.sender,
to: to,
subject: subject,
html: html,
};
transport.sendMail(message, function (err, result) {
transport.close();
callback(err, result);
});
};

View File

@@ -15,11 +15,5 @@
*/
var config = require('../config');
var nfs = config.nfs;
if (!nfs) {
// use qnfs by default
nfs = require('./qnfs');
}
module.exports = nfs;
module.exports = config.nfs;

View File

@@ -1,83 +0,0 @@
/*!
* cnpmjs.org - common/qnfs.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var thunkify = require('thunkify-wrap');
var qn = require('qn');
var fs = require('fs');
var config = require('../config');
var client = qn.create(config.qn);
thunkify(client, ['delete', 'uploadFile', 'upload', 'download']);
exports._client = client;
/**
* Upload file
*
* @param {String} filepath
* @param {Object} options
* - {String} key
* - {Number} size
*/
exports.upload = function *(filepath, options) {
try {
yield client.delete(options.key);
} catch (err) {
// ignore error here
}
var res = yield client.uploadFile(filepath, {
key: options.key,
size: options.size
});
var url = res && res[0] ? res[0].url : '';
return { url: url };
};
exports.uploadBuffer = function *(buf, options) {
try {
yield client.delete(options.key);
} catch (err) {
// ignore error here
}
var res = yield client.upload(buf, {key: options.key});
var url = res && res[0] ? res[0].url : '';
return { url: url };
};
exports.url = function (key) {
return client.resourceURL(key);
};
exports.download = function* (key, filepath, options) {
var writeStream = fs.createWriteStream(filepath);
yield client.download(key, {
timeout: options.timeout,
writeStream: writeStream
});
};
exports.remove = function *(key) {
try {
return yield client.delete(key);
} catch (err) {
if (err.name === 'QiniuFileNotExistsError') {
return;
}
throw err;
}
};

View File

@@ -22,8 +22,7 @@ if (config.redis && config.redis.host && config.redis.port) {
var redis = require('redis');
var wrapper = require('co-redis');
var logger = require('./logger');
var _client = redis.createClient(config.redis);
var _client = redis.createClient(config.redis.port, config.redis.host);
_client.on('error', function (err) {
logger.error(err);

View File

@@ -1,34 +0,0 @@
/*!
* cnpmjs.org - common/session.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';
/**
* Module dependencies.
*/
var middlewares = require('koa-middlewares');
var config = require('../config');
var key = 'AuthSession';
var cookie = { path: '/', httpOnly: true, maxAge: 3600000 * 24 * 365, signed: false };
var options = {
key: key,
cookie: cookie,
defer: true,
rolling: false
};
if (!config.debug) {
options.store = config.sessionStore || middlewares.RedisStore(config.redis);
}
module.exports = middlewares.session(options);

85
common/urllib.js Normal file
View File

@@ -0,0 +1,85 @@
/**!
* cnpmjs.org - common/urllib.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var urllib = require('urllib');
var HttpAgent = require('agentkeepalive');
var HttpsAgent = require('agentkeepalive').HttpsAgent;
var httpAgent = new HttpAgent({
timeout: 0,
keepAliveTimeout: 15000
});
var httpsAgent = new HttpsAgent({
timeout: 0,
keepAliveTimeout: 15000
});
var client = urllib.create({
agent: httpAgent,
httpsAgent: httpsAgent
});
module.exports = client;
module.exports.USER_AGENT = urllib.USER_AGENT;
function startMonitor() {
var statInterval = 60000;
var agents = [
['httpAgent', httpAgent],
['httpsAgent', httpsAgent]
];
function agentStat() {
for (var i = 0; i < agents.length; i++) {
var type = agents[i][0];
var agent = agents[i][1];
var rate = '0';
if (agent.createSocketCount > 0) {
rate = (agent.requestCount / agent.createSocketCount).toFixed(0);
}
console.info('[%s] socket: %d created, %d close, %d timeout, request: %d requests, %s req/socket',
type,
agent.createSocketCount,
agent.closeSocketCount,
agent.timeoutSocketCount,
agent.requestCount,
rate
);
var name;
for (name in agent.sockets) {
console.info('working sockets %s: %d', name, agent.sockets[name].length);
}
for (name in agent.freeSockets) {
console.info('free sockets %s: %d', name, agent.freeSockets[name].length);
}
for (name in agent.requests) {
console.info('pedding requests %s: %d', name, agent.requests[name].length);
}
if (agent.requestCount >= 100000000) {
agent.requestCount = 0;
agent.createSocketCount = 0;
agent.closeSocketCount = 0;
agent.timeoutSocketCount = 0;
}
}
}
agentStat();
return setInterval(agentStat, statInterval);
}
startMonitor();

View File

@@ -28,15 +28,96 @@ var root = path.dirname(__dirname);
var config = {
version: version,
/**
* Cluster mode
*/
enableCluster: false,
numCPUs: os.cpus().length,
/*
* server configure
*/
registryPort: 7001,
webPort: 7002,
bindingHost: '127.0.0.1', // only binding on 127.0.0.1 for local access
enableCluster: false,
numCPUs: os.cpus().length,
debug: true, // if debug
// debug mode
// if in debug mode, some middleware like limit wont load
// logger module will print to stdout
debug: true,
// session secret
sessionSecret: 'cnpmjs.org test session secret',
// max request json body size
jsonLimit: '10mb',
// log dir name
logdir: path.join(root, '.tmp', 'logs'),
// update file template dir
uploadDir: path.join(root, '.dist'),
// web page viewCache
viewCache: false,
// mysql config
// config for koa-limit middleware
// for limit download rates
limit: {
enable: false,
token: 'koa-limit:download',
limit: 1000,
interval: 1000 * 60 * 60 * 24,
whiteList: [],
blackList: [],
message: 'request frequency limited, any question, please contact fengmk2@gmail.com',
},
enableCompress: false, // enable gzip response or not
// default system admins
admins: {
// name: email
fengmk2: 'fengmk2@gmail.com',
admin: 'admin@cnpmjs.org',
dead_horse: 'dead_horse@qq.com',
},
// email notification for errors
// check https://github.com/andris9/Nodemailer for more informations
mail: {
appname: 'cnpmjs.org',
from: 'cnpmjs.org mail sender <adderss@gmail.com>',
service: 'gmail',
auth: {
user: 'address@gmail.com',
pass: 'your password'
}
},
// forward Compat with old style
// mail: {
// appname: 'cnpmjs.org',
// sender: 'cnpmjs.org mail sender <adderss@gmail.com>',
// host: 'smtp.gmail.com',
// port: 465,
// user: 'address@gmail.com',
// pass: 'your password',
// ssl: true,
// debug: false
// },
logoURL: '//ww4.sinaimg.cn/large/69c1d4acgw1ebfly5kjlij208202oglr.jpg', // cnpm logo image url
customReadmeFile: '', // you can use your custom readme file instead the cnpm one
customFooter: '', // you can add copyright and site total script html here
npmClientName: 'cnpm', // use `${name} install package`
packagePageContributorSearch: true, // package page contributor link to search, default is true
// max handle number of package.json `dependencies` property
maxDependencies: 200,
// backup filepath prefix
backupFilePrefix: '/cnpm/backup/',
/**
* mysql config
*/
mysqlServers: [
{
host: '127.0.0.1',
@@ -49,67 +130,96 @@ var config = {
mysqlMaxConnections: 4,
mysqlQueryTimeout: 5000,
sessionSecret: 'cnpmjs.org test session secret',
redis: {
// host: 'pub-redis-19533.us-east-1-4.3.ec2.garantiadata.com',
// port: 19533,
// pass: 'cnpmjs_dev'
},
jsonLimit: '10mb', // max request json body size
uploadDir: path.join(root, '.dist'),
// qiniu cdn: http://www.qiniu.com/, it free for dev.
qn: {
accessKey: "iN7NgwM31j4-BZacMjPrOQBs34UG1maYCAQmhdCV",
secretKey: "6QTOr2Jg1gcZEWDQXKOGZh5PziC2MCV5KsntT70j",
bucket: "qtestbucket",
domain: "http://qtestbucket.qiniudn.com"
},
mail: {
appname: 'cnpmjs.org',
sender: 'cnpmjs.org mail sender <adderss@gmail.com>',
host: 'smtp.gmail.com',
port: 465,
user: 'address@gmail.com',
pass: 'your password',
ssl: true,
debug: false
},
// redis config
// use for koa-limit module as storage
redis: null,
// package tarball store in local filesystem by default
nfs: require('fs-cnpm')({
dir: path.join(root, '.tmp', 'dist')
}),
// registry url name
registryHost: 'r.cnpmjs.org',
/**
* registry mode config
*/
// enable private mode, only admin can publish, other use just can sync package from source npm
enablePrivate: true,
// registry scopes, if don't set, means do not support scopes
scopes: [
'@cnpm',
'@cnpmtest'
],
// redirect @cnpm/private-package => private-package
// forward compatbility for update from lower version cnpmjs.org
adaptScope: true,
// force user publish with scope
// but admins still can publish without scope
forcePublishWithScope: true,
// some registry already have some private packages in global scope
// but we want to treat them as scoped private packages,
// so you can use this white list.
privatePackages: ['private-package'],
/**
* sync configs
*/
// sync dist config
// sync node.js dist from nodejs.org
noticeSyncDistError: true,
disturl: 'http://nodejs.org/dist',
syncDist: false,
logoURL: 'http://ww4.sinaimg.cn/large/69c1d4acgw1ebfly5kjlij208202oglr.jpg',
registryHost: 'r.cnpmjs.org',
// customReadmeFile: __dirname + '/web_readme.md',
customReadmeFile: '', // you can use your custom readme file instead the cnpm one
customFooter: '', // you can add copyright and site total script html here
npmClientName: 'cnpm', // use `${name} install package`
packagePageContributorSearch: true, // package page contributor link to search, default is true
sourceNpmRegistry: 'http://registry.npmjs.org',
enablePrivate: true, // enable private mode, only admin can publish, other use just can sync package from source npm
admins: {
fengmk2: 'fengmk2@gmail.com',
admin: 'admin@cnpmjs.org',
dead_horse: 'dead_horse@qq.com',
cnpmjstest10: 'cnpmjstest10@cnpmjs.org',
},
syncByInstall: true,
backupFilePrefix: '/cnpm/backup/', // backup filepath prefix
syncModel: 'none', // 'none', 'all', 'exist'
syncConcurrency: 1,
syncInterval: '10m', // sync interval, default is 10 minutes
maxDependencies: 200, // max handle number of package.json `dependencies` property
limit: {
enable: false,
token: 'koa-limit:download',
limit: 1000,
interval: 1000 * 60 * 60 * 24,
whiteList: [],
blackList: [],
message: 'request frequency limited, any question, please contact fengmk2@gmail.com',
},
enableCompress: false, // enable gzip response or not
// the official npm registry
// cnpm wont directly sync from this one
// but sometimes will request it for some package infomations
// please don't change it if not necessary
officialNpmRegistry: 'https://registry.npmjs.org',
// sync source, upstream registry
// If you want to directly sync from official npm's registry
// please drop them an email first
sourceNpmRegistry: 'http://registry.npm.taobao.org',
// upstream registry is base on cnpm/cnpmjs.org or not
// if your upstream is official npm registry, please turn it off
sourceNpmRegistryIsCNpm: true,
// if install return 404, try to sync from source registry
syncByInstall: true,
// sync mode select
// none: do not sync any module
// exist: only sync exist modules
// all: sync all modules
syncModel: 'none', // 'none', 'all', 'exist'
syncConcurrency: 1,
// sync interval, default is 10 minutes
syncInterval: '10m',
// sync polular modules, default to false
// because cnpm can't auto sync tag change for now
// so we want to sync popular modules to ensure their tags
syncPopular: false,
syncPopularInterval: '1h',
// top 100
topPopular: 100,
// sync devDependencies or not, default is false
syncDevDependencies: false,
// badge subject on http://shields.io/
badgeSubject: 'cnpm',
};
// load config/config.js, everything in config.js will cover the same key in index.js

View File

@@ -24,9 +24,9 @@ exports.total = function (name, callback) {
name = null;
}
var end = moment();
var start = end.clone().subtract('months', 1).startOf('month');
var lastday = end.clone().subtract('days', 1).format('YYYY-MM-DD');
var lastweekStart = end.clone().subtract('weeks', 1).startOf('week');
var start = end.clone().subtract(1, 'months').startOf('month');
var lastday = end.clone().subtract(1, 'days').format('YYYY-MM-DD');
var lastweekStart = end.clone().subtract(1, 'weeks').startOf('week');
var lastweekEnd = lastweekStart.clone().endOf('week').format('YYYY-MM-DD');
var lastmonthEnd = start.clone().endOf('month').format('YYYY-MM-DD');
var thismonthStart = end.clone().startOf('month').format('YYYY-MM-DD');

View File

@@ -0,0 +1,60 @@
/**!
* cnpmjs.org - controllers/registry/deprecate.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var Module = require('../../proxy/module');
module.exports = deprecateVersions;
/**
* @see https://github.com/cnpm/cnpmjs.org/issues/415
*/
function* deprecateVersions(next) {
var body = this.request.body;
var name = this.params.name || this.params[0];
var tasks = [];
for (var version in body.versions) {
tasks.push(Module.get(name, version));
}
var rs = yield tasks;
var updateTasks = [];
for (var i = 0; i < rs.length; i++) {
var row = rs[i];
if (!row) {
// some version not exists
this.status = 400;
this.body = {
error: 'version_error',
reason: 'Some versions: ' + JSON.stringify(Object.keys(body.versions)) + ' not found'
};
return;
}
var data = body.versions[row.package.version];
if (typeof data.deprecated === 'string') {
row.package.deprecated = data.deprecated;
updateTasks.push(Module.updatePackage(row.id, row.package));
}
}
yield updateTasks;
// update last modified
yield* Module.updateLastModified(name);
this.status = 201;
this.body = {
ok: true
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -18,14 +18,31 @@
var debug = require('debug')('cnpmjs.org:controllers:registry:user');
var utility = require('utility');
var crypto = require('crypto');
var UserService = require('../../services/user');
var User = require('../../proxy/user');
var config = require('../../config');
var common = require('../../lib/common');
exports.show = function *(next) {
exports.show = function* (next) {
var name = this.params.name;
var user = yield User.get(name);
var isAdmin = common.isAdmin(name);
var scopes = config.scopes || [];
if (config.customUserService) {
var customUser = yield* UserService.get(name);
if (customUser) {
isAdmin = !!customUser.site_admin;
scopes = customUser.scopes;
var data = {
user: customUser
};
yield* User.saveCustomUser(data);
}
}
var user = yield* User.get(name);
if (!user) {
return yield *next;
return yield* next;
}
var data = user.json;
@@ -40,12 +57,32 @@ exports.show = function *(next) {
date: user.gmt_modified,
};
}
if (data.login) {
// custom user format
// convert to npm user format
data = {
_id: 'org.couchdb.user:' + user.name,
_rev: user.rev,
name: user.name,
email: user.email,
type: 'user',
roles: [],
date: user.gmt_modified,
avatar: data.avatar_url,
fullname: data.name || data.login,
homepage: data.html_url,
};
}
data._cnpm_meta = {
id: user.id,
npm_user: user.npm_user,
npm_user: user.npm_user === 1,
custom_user: user.npm_user === 2,
gmt_create: user.gmt_create,
gmt_modified: user.gmt_modified,
admin: !!config.admins[user.name],
admin: isAdmin,
scopes: scopes,
};
this.body = data;
@@ -85,20 +122,20 @@ function ensurePasswordSalt(user, body) {
// 'content-length': '258',
// connection: 'keep-alive' }
// { name: 'mk2',
// salt: '18d8d51936478446a5466d4fb1633b80f3838b4caaa03649a885ac722cd6',
// password_sha: '8f4408912a6db1d96b132a90856d99db029cef3d',
// salt: '12351936478446a5466d4fb1633b80f3838b4caaa03649a885ac722cd6',
// password_sha: '123408912a6db1d96b132a90856d99db029cef3d',
// email: 'fengmk2@gmail.com',
// _id: 'org.couchdb.user:mk2',
// type: 'user',
// roles: [],
// date: '2014-03-15T02:39:25.696Z' }
exports.add = function *() {
exports.add = function* () {
var name = this.params.name;
var body = this.request.body || {};
var user = {
name: body.name,
salt: body.salt,
password_sha: body.password_sha,
// salt: body.salt,
// password_sha: body.password_sha,
email: body.email,
ip: this.ip || '0.0.0.0',
// roles: body.roles || [],
@@ -106,7 +143,7 @@ exports.add = function *() {
ensurePasswordSalt(user, body);
if (!user.name || !user.salt || !user.password_sha || !user.email) {
if (!body.password || !user.name || !user.salt || !user.password_sha || !user.email) {
this.status = 422;
this.body = {
error: 'paramError',
@@ -114,7 +151,47 @@ exports.add = function *() {
};
return;
}
debug('add user: %j', user);
debug('add user: %j', body);
var loginedUser;
try {
loginedUser = yield UserService.auth(body.name, body.password);
} catch (err) {
this.status = err.status || 500;
this.body = {
error: err.name,
reason: err.message
};
return;
}
if (loginedUser) {
var rev = Date.now() + '-' + loginedUser.login;
if (config.customUserService) {
// make sure sync user meta to cnpm database
var data = user;
data.rev = rev;
data.user = loginedUser;
yield* User.saveCustomUser(data);
}
this.status = 201;
this.body = {
ok: true,
id: 'org.couchdb.user:' + loginedUser.login,
rev: rev,
};
return;
}
if (config.customUserService) {
// user login fail, not allow to add new user
this.status = 401;
this.body = {
error: 'unauthorized',
reason: 'Login fail, please check your login name and password'
};
return;
}
var existUser = yield User.get(name);
if (existUser) {
@@ -136,24 +213,23 @@ exports.add = function *() {
};
};
exports.authSession = function *() {
// body: {"name":"foo","password":"****"}
var body = this.request.body || {};
var name = body.name;
var password = body.password;
var user = yield User.auth(name, password);
debug('authSession %s: %j', name, user);
if (!user) {
this.status = 401;
this.body = {ok: false, name: null, roles: []};
return;
}
var session = yield *this.session;
session.name = user.name;
this.body = {ok: true, name: user.name, roles: []};
};
// logined before update, no need to auth user again
// { name: 'admin',
// password: '123123',
// email: 'fengmk2@gmail.com',
// _id: 'org.couchdb.user:admin',
// type: 'user',
// roles: [],
// date: '2014-08-05T16:08:22.645Z',
// _rev: '1-1a18c3d73ba42e863523a399ff3304d8',
// _cnpm_meta:
// { id: 14,
// npm_user: false,
// custom_user: false,
// gmt_create: '2014-08-05T15:46:58.000Z',
// gmt_modified: '2014-08-05T15:46:58.000Z',
// admin: true,
// scopes: [ '@cnpm', '@cnpmtest' ] } }
exports.update = function *(next) {
var name = this.params.name;
var rev = this.params.rev;
@@ -175,17 +251,19 @@ exports.update = function *(next) {
var body = this.request.body || {};
var user = {
name: body.name,
salt: body.salt,
password_sha: body.password_sha,
// salt: body.salt,
// password_sha: body.password_sha,
email: body.email,
ip: this.ip || '0.0.0.0',
rev: body.rev || body._rev,
// roles: body.roles || [],
};
debug('update user %j', body);
ensurePasswordSalt(user, body);
if (!user.name || !user.salt || !user.password_sha || !user.email) {
if (!body.password || !user.name || !user.salt || !user.password_sha || !user.email) {
this.status = 422;
this.body = {
error: 'paramError',

View File

@@ -0,0 +1,70 @@
/**!
* cnpmjs.org - controllers/registry/user_package.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var Module = require('../../proxy/module');
var NpmModuleMaintainer = require('../../proxy/npm_module_maintainer');
exports.list = function* () {
var users = this.params.user.split('|');
if (users.length > 20) {
this.status = 400;
this.body = {
error: 'bad_request',
reason: 'reach max user names limit, must <= 20 user names'
};
return;
}
var firstUser = users[0];
if (!firstUser) {
// params.user = '|'
this.body = {};
return;
}
var data = {};
var r = yield [
NpmModuleMaintainer.listByUsers(users),
// get the first user module by author field
Module.listNamesByAuthor(firstUser),
];
var rows = r[0];
var firstUserModuleNames = r[1];
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
if (data[row.user]) {
data[row.user].push(row.name);
} else {
data[row.user] = [row.name];
}
}
if (firstUserModuleNames.length > 0) {
if (!data[firstUser]) {
data[firstUser] = firstUserModuleNames;
} else {
var items = data[firstUser];
for (var i = 0; i < firstUserModuleNames.length; i++) {
var name = firstUserModuleNames[i];
if (items.indexOf(name) === -1) {
items.push(name);
}
}
}
}
this.body = data;
};

View File

@@ -1,5 +1,5 @@
/**!
* cnpmjs.org - controllers/download.js
* cnpmjs.org - controllers/sync.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
@@ -14,14 +14,16 @@
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:controllers:sync');
var Log = require('../proxy/module_log');
var SyncModuleWorker = require('../proxy/sync_module_worker');
exports.sync = function *() {
exports.sync = function* () {
var username = this.user.name || 'anonymous';
var name = this.params.name;
var name = this.params.name || this.params[0];
var publish = this.query.publish === 'true';
var noDep = this.query.nodeps === 'true';
debug('sync %s with query: %j', name, this.query);
if (publish && !this.user.isAdmin) {
this.status = 403;
this.body = {
@@ -36,31 +38,19 @@ exports.sync = function *() {
noDep: noDep,
};
var result = yield SyncModuleWorker.sync(name, username, options);
var logId = yield SyncModuleWorker.sync(name, username, options);
debug('sync %s got log id %j', name, logId);
// friendly 404 reason info
if (result.statusCode === 404) {
this.status = 404;
this.body = {
ok: false,
reason: 'can not found ' + name + ' in the source registry'
};
return;
}
if (!result.ok) {
this.status = result.statusCode || 500;
this.body = result.pkg;
return;
}
this.status = 201;
this.body = {
ok: true,
logId: result.logId
logId: logId
};
};
exports.getSyncLog = function *(next) {
var logId = this.params.id;
exports.getSyncLog = function* (next) {
// params: [$name, $id] on scope package
var logId = this.params.id || this.params[1];
var offset = Number(this.query.offset) || 0;
var row = yield Log.get(logId);
if (!row) {

View File

@@ -32,7 +32,7 @@ exports.show = function *() {
total.instance_start_time = startTime;
total.node_version = process.version;
total.app_version = version;
total.donate = 'https://me.alipay.com/imk2';
total.donate = 'https://www.gittip.com/fengmk2';
total.sync_model = config.syncModel;
this.body = total;

View File

@@ -18,7 +18,7 @@ var debug = require('debug')('cnpmjs.org:controllers:utils');
var path = require('path');
var fs = require('fs');
var utility = require('utility');
var ms = require('ms');
var ms = require('humanize-ms');
var nfs = require('../common/nfs');
var config = require('../config');

49
controllers/web/badge.js Normal file
View File

@@ -0,0 +1,49 @@
/**!
* cnpmjs.org - controllers/web/badge.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var config = require('../../config');
var Module = require('../../proxy/module');
exports.version = function* (next) {
var color = 'lightgrey';
var version = 'invalid';
var name = this.params[0];
var latestTag = yield* Module.getTag(name, 'latest');
if (latestTag) {
version = latestTag.version;
if (/^0\.0\./.test(version)) {
// <0.1.0 & >=0.0.0
color = 'red';
} else if (/^0\./.test(version)) {
// <1.0.0 & >=0.1.0
color = 'green';
} else {
// >=1.0.0
color = 'blue';
}
}
var subject = config.badgeSubject.replace(/\-/g, '--');
version = version.replace(/\-/g, '--');
var url = 'https://img.shields.io/badge/' + subject + '-' + version + '-' + color + '.svg';
if (this.querystring) {
url += '?' + this.querystring;
} else {
url += '?style=flat-square';
}
this.redirect(url);
};

View File

@@ -16,6 +16,7 @@
var debug = require('debug')('cnpmjs.org:controllers:web:dist');
var mime = require('mime');
var urlparse = require('url').parse;
var Dist = require('../../proxy/dist');
var config = require('../../config');
var downloadAsReadStream = require('../utils').downloadAsReadStream;
@@ -44,6 +45,14 @@ exports.list = function* (next) {
}
var items = yield* Dist.listdir(url);
if (url === '/') {
// phantomjs/
items.push({
name: 'phantomjs/',
date: '',
});
}
yield this.render('dist', {
title: 'Mirror index of ' + config.disturl + url,
disturl: config.disturl,
@@ -53,7 +62,7 @@ exports.list = function* (next) {
});
};
function* download(next) {
function* download(next) {
var fullname = this.params[0];
var info = yield* Dist.getfile(fullname);
debug('download %s got %j', fullname, info);
@@ -61,17 +70,31 @@ exports.list = function* (next) {
return yield* next;
}
if (/\.(html|js|css|json|txt)$/.test(fullname)) {
if (info.url.indexOf('http') === 0) {
info.url = urlparse(info.url).path;
}
return yield* pipe.call(this, info, false);
}
if (info.url.indexOf('http') === 0) {
return this.redirect(info.url);
}
yield* pipe.call(this, info, true);
}
function* pipe(info, attachment) {
debug('pipe %j, attachment: %s', info, attachment);
// download it from nfs
if (typeof info.size === 'number') {
if (typeof info.size === 'number' && info.size > 0) {
this.length = info.size;
}
this.type = mime.lookup(info.url);
this.attachment = info.name;
this.etag = info.sha1;
if (attachment) {
this.attachment(info.name);
}
if (info.sha1) {
this.etag = info.sha1;
}
this.body = yield* downloadAsReadStream(info.url);
}

View File

@@ -14,6 +14,7 @@
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:controllers:web:package');
var bytes = require('bytes');
var giturl = require('giturl');
var moment = require('moment');
@@ -30,11 +31,17 @@ var Log = require('../../proxy/module_log');
var ModuleDeps = require('../../proxy/module_deps');
var setDownloadURL = require('../../lib/common').setDownloadURL;
var ModuleStar = require('../../proxy/module_star');
var packageService = require('../../services/package');
var ModuleUnpublished = require('../../proxy/module_unpublished');
exports.display = function *(next) {
exports.display = function* (next) {
var params = this.params;
var name = params.name;
var tag = params.version;
// normal: {name: $name, version: $version}
// scope: [$name, $version]
var orginalName = params.name || params[0];
var name = orginalName;
var tag = params.version || params[1];
debug('display %s with %j', name, params);
var getPackageMethod;
var getPackageArgs;
@@ -46,22 +53,55 @@ exports.display = function *(next) {
getPackageMethod = 'getByTag';
getPackageArgs = [name, tag || 'latest'];
}
var pkg = yield Module[getPackageMethod].apply(Module, getPackageArgs);
if (!pkg) {
var adaptName = yield* Module.getAdaptName(name);
if (adaptName) {
name = adaptName;
pkg = yield Module[getPackageMethod].apply(Module, [name, getPackageArgs[1]]);
}
}
if (!pkg || !pkg.package) {
// check if unpublished
var unpublishedInfo = yield* ModuleUnpublished.get(name);
debug('show unpublished %j', unpublishedInfo);
if (unpublishedInfo) {
var data = {
name: name,
unpublished: unpublishedInfo.package
};
data.unpublished.time = new Date(data.unpublished.time);
if (data.unpublished.maintainers) {
for (var i = 0; i < data.unpublished.maintainers.length; i++) {
var maintainer = data.unpublished.maintainers[i];
if (maintainer.email) {
maintainer.gravatar = gravatar.url(maintainer.email, {s: '50', d: 'retro'}, true);
}
}
}
yield this.render('package_unpublished', {
package: data
});
return;
}
return yield* next;
}
var r = yield [
Module[getPackageMethod].apply(Module, getPackageArgs),
down.total(name),
ModuleDeps.list(name),
ModuleStar.listUsers(name),
packageService.listMaintainers(name)
];
var pkg = r[0];
var download = r[1];
var dependents = (r[2] || []).map(function (item) {
var download = r[0];
var dependents = (r[1] || []).map(function (item) {
return item.deps;
});
var users = r[3];
if (!pkg || !pkg.package) {
return yield* next;
}
var users = r[2];
var maintainers = r[3];
pkg.package.fromNow = moment(pkg.publish_time).fromNow();
pkg = pkg.package;
@@ -71,11 +111,15 @@ exports.display = function *(next) {
pkg.readme = pkg.description || '';
}
if (maintainers.length > 0) {
pkg.maintainers = maintainers;
}
if (pkg.maintainers) {
for (var i = 0; i < pkg.maintainers.length; i++) {
var maintainer = pkg.maintainers[i];
if (maintainer.email) {
maintainer.gravatar = gravatar.url(maintainer.email, {s: '50', d: 'retro'}, false);
maintainer.gravatar = gravatar.url(maintainer.email, {s: '50', d: 'retro'}, true);
}
}
}
@@ -88,7 +132,7 @@ exports.display = function *(next) {
for (var i = 0; i < pkg.contributors.length; i++) {
var contributor = pkg.contributors[i];
if (contributor.email) {
contributor.gravatar = gravatar.url(contributor.email, {s: '50', d: 'retro'}, false);
contributor.gravatar = gravatar.url(contributor.email, {s: '50', d: 'retro'}, true);
}
if (config.packagePageContributorSearch || !contributor.url) {
contributor.url = '/~' + encodeURIComponent(contributor.name);
@@ -113,6 +157,45 @@ exports.display = function *(next) {
pkg.dist.size = bytes(pkg.dist.size || 0);
}
if (pkg.name !== orginalName) {
pkg.name = orginalName;
}
// pkg.engines = {
// "python": ">= 0.11.9",
// "node": ">= 0.11.9",
// "node1": ">= 0.8.9",
// "node2": ">= 0.10.9",
// "node3": ">= 0.6.9",
// };
if (pkg.engines) {
for (var k in pkg.engines) {
var engine = String(pkg.engines[k] || '').trim();
var color = 'blue';
if (k.indexOf('node') === 0) {
color = 'yellowgreen';
var version = /(\d+\.\d+\.\d+)/.exec(engine);
if (version) {
version = version[0];
if (/^0\.11\.\d+/.test(version)) {
color = 'red';
} else if (/^0\.10\./.test(version) ||
/^0\.12\./.test(version) ||
/^0\.14\./.test(version) ||
/^[^0]+\./.test(version)) {
color = 'brightgreen';
}
}
}
pkg.engines[k] = {
version: engine,
title: k + ': ' + engine,
badgeURL: 'https://img.shields.io/badge/' + encodeURIComponent(k) +
'-' + encodeURIComponent(engine) + '-' + color + '.svg?style=flat-square',
};
}
}
yield this.render('package', {
title: 'Package - ' + pkg.name,
package: pkg,
@@ -122,7 +205,8 @@ exports.display = function *(next) {
exports.search = function *(next) {
var params = this.params;
var word = params.word;
var word = params.word || params[0];
debug('search %j', word);
var result = yield Module.search(word);
var match = null;
@@ -185,14 +269,22 @@ exports.rangeSearch = function *(next) {
};
};
exports.displaySync = function *(next) {
var name = this.params.name || this.query.name;
exports.displaySync = function* (next) {
var name = this.params.name || this.params[0] || this.query.name;
yield this.render('sync', {
name: name,
title: 'Sync - ' + name
title: 'Sync - ' + name,
});
};
exports.listPrivates = function* () {
var packages = yield Module.listPrivates();
yield this.render('private', {
title: 'private packages',
packages: packages
});
};
function setLicense(pkg) {
var license;
license = pkg.license || pkg.licenses || pkg.licence || pkg.licences;

View File

@@ -1,4 +1,4 @@
/*!
/**!
* cnpmjs.org - controllers/web/package.js
*
* Copyright(c) cnpmjs.org and other contributors.
@@ -6,6 +6,7 @@
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
@@ -13,26 +14,69 @@
/**
* Module dependencies.
*/
var config = require('../../config');
var Module = require('../../proxy/module');
var User = require('../../proxy/user');
var UserService = require('../../services/user');
var common = require('../../lib/common');
exports.display = function *(next) {
exports.display = function* (next) {
var name = this.params.name;
var isAdmin = common.isAdmin(name);
var scopes = config.scopes || [];
if (config.customUserService) {
var customUser = yield* UserService.get(name);
if (customUser) {
isAdmin = !!customUser.site_admin;
scopes = customUser.scopes;
var data = {
user: customUser
};
yield* User.saveCustomUser(data);
}
}
var r = yield [Module.listByAuthor(name), User.get(name)];
var packages = r[0];
var packages = r[0] || [];
var user = r[1];
if (!user && !packages.length) {
return yield* next;
}
user = {
user = user || {};
var data = {
name: name,
email: user && user.email
email: user.email,
json: user.json || {}
};
if (data.json.login) {
// custom user format
// convert to npm user format
var json = data.json;
data.json = {
_id: 'org.couchdb.user:' + user.name,
_rev: user.rev,
name: user.name,
email: user.email,
type: 'user',
roles: [],
date: user.gmt_modified,
avatar: json.avatar_url,
fullname: json.name || json.login,
homepage: json.html_url,
im: json.im_url
};
}
yield this.render('profile', {
title: 'User - ' + name,
packages: packages || [],
user: user
packages: packages,
user: data,
lastModified: user && user.gmt_modified,
isAdmin: isAdmin,
scopes: scopes
});
};

View File

@@ -18,41 +18,50 @@
var path = require('path');
var util = require('util');
var cluster = require('cluster');
var cfork = require('cfork');
var config = require('./config');
var workerPath = path.join(__dirname, 'worker.js');
var childProcess = require('child_process');
var syncPath = path.join(__dirname, 'sync');
if (config.enableCluster) {
cluster.setupMaster({
exec: workerPath
});
forkWorker();
if (config.syncModel !== 'none') {
forkSyncer();
}
} else {
require(workerPath);
if (config.syncModel !== 'none') {
require(syncPath);
}
}
cluster.on('fork', function (worker) {
function forkWorker() {
cfork({
exec: workerPath,
count: config.numCPUs,
}).on('fork', function (worker) {
console.log('[%s] [worker:%d] new worker start', Date(), worker.process.pid);
});
cluster.on('disconnect', function (worker) {
var w = cluster.fork();
console.error('[%s] [master:%s] wroker:%s disconnect, suicide: %s, state: %s. New worker:%s fork',
Date(), process.pid, worker.process.pid, worker.suicide, worker.state, w.process.pid);
});
cluster.on('exit', function (worker, code, signal) {
}).on('disconnect', function (worker) {
console.error('[%s] [master:%s] wroker:%s disconnect, suicide: %s, state: %s.',
Date(), process.pid, worker.process.pid, worker.suicide, worker.state);
}).on('exit', function (worker, code, signal) {
var exitCode = worker.process.exitCode;
var err = new Error(util.format('worker %s died (code: %s, signal: %s, suicide: %s, state: %s)',
worker.process.pid, exitCode, signal, worker.suicide, worker.state));
err.name = 'WorkerDiedError';
console.error('[%s] [master:%s] wroker exit: %s', Date(), process.pid, err.stack);
});
// Fork workers.
for (var i = 0; i < config.numCPUs; i++) {
cluster.fork();
}
childProcess.fork(syncPath);
} else {
require(workerPath);
require(syncPath);
}
function forkSyncer() {
var syncer = childProcess.fork(syncPath);
syncer.on('exit', function (code, signal) {
var err = new Error(util.format('syncer %s died (code: %s, signal: %s, stdout: %s, stderr: %s)',
syncer.pid, code, signal, syncer.stdout, syncer.stderr));
err.name = 'SyncerWorkerDiedError';
console.error('[%s] [master:%s] syncer exit: %s: %s',
Date(), process.pid, err.name, err.message);
setTimeout(forkSyncer, 1000);
});
}

View File

@@ -1,4 +1,4 @@
CREATE TABLE `user` (
CREATE TABLE IF NOT EXISTS `user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
@@ -19,7 +19,7 @@ CREATE TABLE `user` (
-- ADD `json` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'json details',
-- ADD `npm_user` tinyint(1) DEFAULT '0' COMMENT 'user sync from npm or not, 1: true, other: false';
CREATE TABLE `module_keyword` (
CREATE TABLE IF NOT EXISTS `module_keyword` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`keyword` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'keyword',
@@ -30,7 +30,7 @@ CREATE TABLE `module_keyword` (
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module keyword';
CREATE TABLE `module_star` (
CREATE TABLE IF NOT EXISTS `module_star` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name',
@@ -40,7 +40,27 @@ CREATE TABLE `module_star` (
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module star';
CREATE TABLE `module` (
CREATE TABLE IF NOT EXISTS `module_maintainer` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name',
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
PRIMARY KEY (`id`),
UNIQUE KEY `user_module_name` (`user`,`name`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='private module maintainers';
CREATE TABLE IF NOT EXISTS `npm_module_maintainer` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name',
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
PRIMARY KEY (`id`),
UNIQUE KEY `user_module_name` (`user`,`name`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='npm original module maintainers';
CREATE TABLE IF NOT EXISTS `module` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
@@ -66,7 +86,7 @@ CREATE TABLE `module` (
-- show create table module\G
-- ALTER TABLE `module` CHANGE `name` `name` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
CREATE TABLE `module_log` (
CREATE TABLE IF NOT EXISTS `module_log` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
@@ -78,7 +98,7 @@ CREATE TABLE `module_log` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module sync log';
-- ALTER TABLE `module_log` CHANGE `name` `name` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
CREATE TABLE `tag` (
CREATE TABLE IF NOT EXISTS `tag` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
@@ -94,7 +114,18 @@ CREATE TABLE `tag` (
-- ALTER TABLE `tag` CHANGE `name` `name` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
-- ALTER TABLE `tag` ADD KEY `gmt_modified` (`gmt_modified`);
CREATE TABLE `total` (
CREATE TABLE IF NOT EXISTS `module_unpublished` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`package` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'base info: tags, time, maintainers, description, versions',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
KEY `gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module unpublished info';
CREATE TABLE IF NOT EXISTS `total` (
`name` varchar(100) NOT NULL COMMENT 'total name',
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
`module_delete` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'module delete count',
@@ -108,7 +139,9 @@ CREATE TABLE `total` (
`last_sync_module` varchar(100) COMMENT 'last sync success module name',
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='total info';
INSERT INTO total(name, gmt_modified) VALUES('total', now());
-- init `total` count
INSERT INTO total(name, gmt_modified) VALUES('total', now())
ON DUPLICATE KEY UPDATE gmt_modified=now();
-- ALTER TABLE `total` ADD `last_sync_time` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'last timestamp sync from official registry'
-- ALTER TABLE `total` ADD `last_exist_sync_time` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'last timestamp sync exist packages from official registry'
-- ALTER TABLE `total` ADD `sync_status` tinyint unsigned NOT NULL DEFAULT '0' COMMENT 'system sync from official registry status'
@@ -118,7 +151,7 @@ INSERT INTO total(name, gmt_modified) VALUES('total', now());
-- ALTER TABLE `total` ADD `left_sync_num` int unsigned NOT NULL DEFAULT '0' COMMENT 'how many packages left to be sync'
-- ALTER TABLE `total` ADD `last_sync_module` varchar(100) NOT NULL COMMENT 'last sync success module name';
CREATE TABLE `download_total` (
CREATE TABLE IF NOT EXISTS `download_total` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
@@ -130,7 +163,7 @@ CREATE TABLE `download_total` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module download total info';
-- ALTER TABLE `download_total` CHANGE `name` `name` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
CREATE TABLE `module_deps` (
CREATE TABLE IF NOT EXISTS `module_deps` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
@@ -140,7 +173,7 @@ CREATE TABLE `module_deps` (
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module deps';
CREATE TABLE `dist_dir` (
CREATE TABLE IF NOT EXISTS `dist_dir` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
@@ -152,7 +185,7 @@ CREATE TABLE `dist_dir` (
KEY `gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='dist dir info';
CREATE TABLE `dist_file` (
CREATE TABLE IF NOT EXISTS `dist_file` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`gmt_modified` datetime NOT NULL COMMENT 'modified time',

View File

@@ -1,27 +0,0 @@
-- http://nodejs.org/dist/ mirror
CREATE TABLE `dist_dir` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
`name` varchar(200) NOT NULL COMMENT 'user name',
`parent` varchar(200) NOT NULL COMMENT 'parent dir' DEFAULT '/',
`date` varchar(20) COMMENT '02-May-2014 01:06',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`parent`, `name`),
KEY `gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='dist dir info';
CREATE TABLE `dist_file` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
`name` varchar(100) NOT NULL COMMENT 'user name',
`parent` varchar(200) NOT NULL COMMENT 'parent dir' DEFAULT '/',
`date` varchar(20) COMMENT '02-May-2014 01:06',
`size` int(10) unsigned NOT NULL COMMENT 'file size' DEFAULT '0',
`sha1` varchar(40) COMMENT 'sha1 hex value',
`url` varchar(2048),
PRIMARY KEY (`id`),
UNIQUE KEY `fullname` (`parent`, `name`),
KEY `gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='dist file info';

895
docs/registry-api.md Normal file
View File

@@ -0,0 +1,895 @@
# NPM Registry API
## Overview
* [Schema](/docs/registry-api.md#schema)
* [Client Errors](/docs/registry-api.md#client-errors)
* [Authentication](/docs/registry-api.md#authentication)
* [Package](/docs/registry-api.md#package)
* [User](/docs/registry-api.md#user)
* [Search](/docs/registry-api.md#search)
## Schema
All API access is over HTTPS or HTTP,
and accessed from the `registry.npmjs.org` domain.
All data is sent and received as JSON.
```bash
$ curl -i https://registry.npmjs.org
HTTP/1.1 200 OK
Date: Tue, 05 Aug 2014 10:53:24 GMT
Server: CouchDB/1.5.0 (Erlang OTP/R16B03)
Content-Type: text/plain; charset=utf-8
Cache-Control: max-age=60
Content-Length: 258
Accept-Ranges: bytes
Via: 1.1 varnish
Age: 11
X-Served-By: cache-ty67-TYO
X-Cache: HIT
X-Cache-Hits: 1
X-Timer: S1407236004.867906,VS0,VE0
{"db_name":"registry","doc_count":90789,"doc_del_count":381,"update_seq":137250,"purge_seq":0,
"compact_running":false,"disk_size":436228219,"data_size":332875061,
"instance_start_time":"1405721973718703","disk_format_version":6,"committed_update_seq":137250}
```
## Client Errors
```json
Status: 4xx
{
"error": "error_name",
"reason": "error reason string"
}
```
## Authentication
There is only one way to authenticate through the API.
## Basic Authentication
```bash
$ curl -u "username:password" https://registry.npmjs.org
```
## Failed login limit
```bash
$ curl -i -X PUT -u foo:pwd \
-d '{"name":"foo","email":"foo@bar.com","type":"user","roles":[]}' \
https://registry.npmjs.org/-/user/org.couchdb.user:foo/-rev/11-d226c6afa9286ab5b9eb858c429bdabf
HTTP/1.1 401 Unauthorized
Date: Tue, 05 Aug 2014 15:33:25 GMT
Server: CouchDB/1.5.0 (Erlang OTP/R14B04)
Content-Type: text/plain; charset=utf-8
Cache-Control: max-age=60
Content-Length: 67
Accept-Ranges: bytes
Via: 1.1 varnish
X-Served-By: cache-ty66-TYO
X-Cache: MISS
X-Cache-Hits: 0
X-Timer: S1407252805.261390,VS0,VE434
{"error":"unauthorized","reason":"Name or password is incorrect."}
```
## Package
* Read
* [Get a single package](/docs/registry-api.md#get-a-single-package)
* [Get a special version or tag package](/docs/registry-api.md#get-a-special-version-or-tag-package)
* [List packages since from a update time](/docs/registry-api.md#list-packages-since-from-a-update-time)
* [List package names by users](/docs/registry-api.md#list-package-names-by-users)
* Write
* [Publish a new package](/docs/registry-api.md#publish-a-new-package)
* [Update a package's tag](/docs/registry-api.md#update-a-packages-tag)
* [Update a package's maintainers](/docs/registry-api.md#update-a-packages-maintainers)
* [Remove one version from package](/docs/registry-api.md#remove-one-version-from-package)
* [Remove a tgz file from package](/docs/registry-api.md#remove-a-tgz-file-from-package)
### Get a single package
```
GET /:package
```
#### Response
```json
HTTP/1.1 200 OK
Etag: "8UDCP753LFXOG42NMX88JAN40"
Content-Type: application/json
Cache-Control: max-age=60
Content-Length: 2243
{
"_id": "pedding",
"_rev": "11-e6d1e6e96eaf72433fef6aaabe843af8",
"name": "pedding",
"description": "Just pedding for callback.",
"dist-tags": {
"latest": "1.0.0"
},
"versions": {
"1.0.0": {
"name": "pedding",
"version": "1.0.0",
"description": "Just pedding for callback.",
"main": "index.js",
"scripts": {
"test": "make test-all"
},
"repository": {
"type": "git",
"url": "git://github.com/fengmk2/pedding.git"
},
"keywords": [
"pedding",
"callback"
],
"devDependencies": {
"contributors": "*",
"mocha": "*",
"mocha-phantomjs": "*",
"component": "*",
"chai": "*"
},
"author": {
"name": "fengmk2",
"email": "fengmk2@gmail.com"
},
"license": "MIT",
"contributors": [
{
"name": "fengmk2",
"email": "fengmk2@gmail.com",
"url": "https://github.com/fengmk2"
},
{
"name": "dead-horse",
"email": "dead_horse@qq.com",
"url": "https://github.com/dead-horse"
}
],
"gitHead": "b42a708414a704336e9dee570a963e2dbe43e529",
"bugs": {
"url": "https://github.com/fengmk2/pedding/issues"
},
"homepage": "https://github.com/fengmk2/pedding",
"_id": "pedding@1.0.0",
"_shasum": "7f5098d60307b4ef7240c3d693cb20a9473c6074",
"_from": ".",
"_npmVersion": "1.4.13",
"_npmUser": {
"name": "fengmk2",
"email": "fengmk2@gmail.com"
},
"maintainers": [
{
"name": "fengmk2",
"email": "fengmk2@gmail.com"
},
{
"name": "dead-horse",
"email": "dead_horse@qq.com"
}
],
"dist": {
"shasum": "7f5098d60307b4ef7240c3d693cb20a9473c6074",
"tarball": "http://registry.npmjs.org/pedding/-/pedding-1.0.0.tgz"
},
"directories": {}
}
},
"readme": "# pedding\n readme...",
"maintainers": [
{
"name": "fengmk2",
"email": "fengmk2@gmail.com"
},
{
"name": "dead-horse",
"email": "dead_horse@qq.com"
},
{
"name": "dead_horse",
"email": "dead_horse@qq.com"
}
],
"time": {
"modified": "2014-07-05T14:22:53.849Z",
"created": "2012-09-18T14:46:08.346Z",
"0.0.1": "2012-09-18T14:46:21.321Z",
"0.0.2": "2013-06-22T08:26:45.125Z",
"0.0.3": "2013-07-02T15:20:34.707Z",
"1.0.0": "2014-07-05T11:08:51.614Z"
},
"author": {
"name": "fengmk2",
"email": "fengmk2@gmail.com"
},
"repository": {
"type": "git",
"url": "git://github.com/fengmk2/pedding.git"
},
"keywords": [
"pedding",
"callback"
],
"bugs": {
"url": "https://github.com/fengmk2/pedding/issues"
},
"license": "MIT",
"readmeFilename": "README.md",
"homepage": "https://github.com/fengmk2/pedding",
"contributors": [
{
"name": "fengmk2",
"email": "fengmk2@gmail.com",
"url": "https://github.com/fengmk2"
},
{
"name": "dead-horse",
"email": "dead_horse@qq.com",
"url": "https://github.com/dead-horse"
}
],
"_attachments": {}
}
```
### ~~Get a special version or tag package~~
__deprecated__
```
GET /:package/:tag_or_version
```
#### Reponse
```json
HTTP/1.1 200 OK
Etag: "1WJ4JF535RO3BDZR2BARXSGLY"
Content-Type: application/json
Cache-Control: max-age=60
Content-Length: 1183
{
"name": "pedding",
"version": "1.0.0",
"description": "Just pedding for callback.",
"main": "index.js",
"scripts": {
"test": "make test-all"
},
"repository": {
"type": "git",
"url": "git://github.com/fengmk2/pedding.git"
},
"keywords": [
"pedding",
"callback"
],
"devDependencies": {
"contributors": "*",
"mocha": "*",
"mocha-phantomjs": "*",
"component": "*",
"chai": "*"
},
"author": {
"name": "fengmk2",
"email": "fengmk2@gmail.com"
},
"license": "MIT",
"contributors": [
{
"name": "fengmk2",
"email": "fengmk2@gmail.com",
"url": "https://github.com/fengmk2"
},
{
"name": "dead-horse",
"email": "dead_horse@qq.com",
"url": "https://github.com/dead-horse"
}
],
"gitHead": "b42a708414a704336e9dee570a963e2dbe43e529",
"bugs": {
"url": "https://github.com/fengmk2/pedding/issues"
},
"homepage": "https://github.com/fengmk2/pedding",
"_id": "pedding@1.0.0",
"_shasum": "7f5098d60307b4ef7240c3d693cb20a9473c6074",
"_from": ".",
"_npmVersion": "1.4.13",
"_npmUser": {
"name": "fengmk2",
"email": "fengmk2@gmail.com"
},
"maintainers": [
{
"name": "fengmk2",
"email": "fengmk2@gmail.com"
},
{
"name": "dead-horse",
"email": "dead_horse@qq.com"
}
],
"dist": {
"shasum": "7f5098d60307b4ef7240c3d693cb20a9473c6074",
"tarball": "http://registry.npmjs.org/pedding/-/pedding-1.0.0.tgz"
},
"directories": {}
}
```
### Publish a new package
* Authentication required.
```
PUT /:package
```
#### Input
```json
{
"_id": "pedding",
"name": "pedding",
"description": "Just pedding for callback.",
"dist-tags": {
"latest": "1.0.0"
},
"versions": {
"1.0.0": {
"name": "pedding",
"version": "1.0.0",
"description": "Just pedding for callback.",
"main": "index.js",
"scripts": {
"test": "make test-all"
},
"repository": {
"type": "git",
"url": "git://github.com/fengmk2/pedding.git"
},
"keywords": [ "pedding","callback" ],
"devDependencies": {
"contributors": "*",
"mocha": "*",
"mocha-phantomjs": "*",
"component": "*",
"chai": "*"
},
"dependencies": {},
"author": {
"name": "fengmk2",
"email": "fengmk2@gmail.com"
},
"license": "MIT",
"contributors": [
{
"name": "fengmk2",
"email": "fengmk2@gmail.com",
"url": "https://github.com/fengmk2"
},
{
"name": "dead-horse",
"email": "dead_horse@qq.com",
"url": "https://github.com/dead-horse"
}
],
"readme": "# pedding ...",
"readmeFilename": "README.md",
"gitHead": "b42a708414a704336e9dee570a963e2dbe43e529",
"bugs": {
"url": "https://github.com/fengmk2/pedding/issues"
},
"homepage": "https://github.com/fengmk2/pedding",
"_id": "pedding@1.0.0",
"_shasum": "7f5098d60307b4ef7240c3d693cb20a9473c6074",
"_from": ".",
"_npmVersion": "1.5.0-alpha-4",
"_npmUser": {
"name": "admin",
"email": "fengmk2@gmail.com"
},
"maintainers": [
{
"name": "admin",
"email": "fengmk2@gmail.com"
}
],
"dist": {
"shasum": "7f5098d60307b4ef7240c3d693cb20a9473c6074",
"tarball": "https://registry.npmjs.org/pedding/-/pedding-1.0.0.tgz"
}
}
},
"readme": "# pedding ...",
"maintainers": [
{
"name": "admin",
"email": "fengmk2@gmail.com"
}
],
"_attachments": {
"pedding-1.0.0.tgz":{
"content_type": "application/octet-stream",
"data": "H4sIAAAAAAAAA+0aa3PbNjKf8Su...",
"length": 2107
}
}
}
```
#### Response
```json
Status: 201 Created
{
"ok": true,
"rev": "11-e6d1e6e96eaf72433fef6aaabe843af8"
}
```
### Update a package's tag
* Authentication required.
```
PUT /:package/:tag
```
#### Input
The total input body is the `version` string which's setting to the tag.
```json
"1.0.0"
```
#### Response
```json
Status: 201 Created
{
"ok": true
}
```
### Update a package's maintainers
* Authentication required.
```
PUT /:package/-rev/:rev
```
#### Input
```json
{
"_id": "pedding",
"_rev": "11-e6d1e6e96eaf72433fef6aaabe843af8",
"maintainers":[
{ "name": "fengmk2", "email": "fengmk2@gmail.com" },
{ "name": "dead-horse", "email": "dead_horse@qq.com" }
]
}
```
#### Response
```json
Status: 201 Created
{
"ok": true,
"id": "pedding",
"rev": "12-bb300a90c9aeb779748b83ec1b744039"
}
```
### Remove one version from package
* Authentication required.
* In any delete, note that __the version number still cannot be reused__.
```
PUT /:package/-rev/:rev
```
#### Input
Remove that specific version from the versions hash in the `PUT` body.
Example for removing `0.0.1` version:
```json
{
"_id": "pedding",
"_rev": "12-bb300a90c9aeb779748b83ec1b744039",
"name": "pedding",
"description": "desc",
"dist-tags": { "latest": "1.0.0" },
"maintainers":
[ ... ],
"time":
{ ... },
"users": {},
"author": { ... },
"repository": { ... },
"versions":
{ "1.0.0":
{ ... },
"0.0.3":
{ ... },
"0.0.2":
{ ... } },
"readme": "...",
"homepage": "https://github.com/fengmk2/pedding",
"bugs": { ... },
"license": "MIT" }
```
#### Response
```json
Status: 201 Created
{
"ok": true
}
```
### Remove all versions of a package
* Authentication required.
* In any delete, note that __the version number still cannot be reused__.
```
DELETE /:package/-rev/:rev
```
#### Response
```json
Status: 201 Created
{
"ok": true
}
```
### Remove a tgz file from package
* Authentication required.
```
DELETE /:tgzfilepath/-rev/:rev
```
Exmaple for removing `https://registry.npmjs.org/pedding/-/pedding-0.0.1.tgz` file:
```
DELETE /pedding/-/pedding-0.0.1.tgz/-rev/12-bb300a90c9aeb779748b83ec1b744039
```
#### Response
```json
Status: 200 OK
{
"ok": true
}
```
### List packages since from a update time
```
GET /-/all/since?stale=update_after&startkey=:startkey
```
* `startkey` is a ms timestamp
#### Response
```bash
$ curl -i "https://registry.npmjs.org/-/all/since?stale=update_after&startkey=1407255748643"
```
```json
HTTP/1.1 200 OK
{
"_updated": 1407255883282,
"bacon-and-eggs": {
"name": "bacon-and-eggs",
"description": "A functional reactive Twitter API client in node",
"dist-tags": {
"latest": "0.0.4"
},
"maintainers": [
{
"name": "mikegroseclose",
"email": "mike.groseclose@gmail.com"
}
],
"homepage": "http://github.com/mikegroseclose/bacon-and-eggs",
"keywords": [
"twitter",
"api",
"frp",
"functional",
"reactive",
"bacon",
"eggs",
"oauth",
"stream",
"streams"
],
"repository": {
"type": "git",
"url": "git://github.com/mikegroseclose/gulp-regex-replace.git"
},
"author": {
"name": "Mike Groseclose",
"email": "mike.groseclose@gmail.com",
"url": "http://mikegroseclose.com"
},
"bugs": {
"url": "https://github.com/mikegroseclose/gulp-regex-replace/issues"
},
"readmeFilename": "README.md",
"time": {
"modified": "2014-08-05T16:21:17.041Z"
},
"versions": {
"0.0.4": "latest"
}
},
"git-perm-rm": {
"name": "git-perm-rm",
"description": "Permanently remove a file or directory from a git repo including all related commit records.",
"dist-tags": {
"latest": "1.0.1"
},
"maintainers": [
{
"name": "kael",
"email": "i@kael.me"
}
],
"homepage": "https://github.com/kaelzhang/git-perm-rm",
"keywords": [
"git",
"rm",
"git-perm-rm",
"remove",
"permanently"
],
"repository": {
"type": "git",
"url": "git://github.com/kaelzhang/git-perm-rm.git"
},
"author": {
"name": "Kael"
},
"bugs": {
"url": "https://github.com/kaelzhang/git-perm-rm/issues"
},
"license": "MIT",
"readmeFilename": "README.md",
"time": {
"modified": "2014-08-05T16:22:41.253Z"
},
"versions": {
"1.0.1": "latest"
}
}
}
```
### List package names by users
```bash
GET /-/by-user/$username[|$another1[|$another2...]]
```
* `username` user name like `fengmk2`
* also support multi users by `name1|name2|name3`
#### Response
```bash
$ curl -i "https://registry.npmjs.org/-/by-user/czy88840616"
$ curl -i "https://registry.npmjs.org/-/by-user/czy88840616|fengmk2|dead-horse"
```
```json
HTTP/1.1 200 OK
{
"czy88840616": [
"easyconf",
"egg",
"flag",
"gdp",
"generator-webx-vm",
"magic-cube",
"rim",
"tbuild",
"test-publish",
"velocity-parser",
"vmarket",
"wi"
]
}
```
## User
* [Get a single user](/docs/registry-api.md#get-a-single-user)
* [Add a new user](/docs/registry-api.md#add-a-new-user)
* [Update a exists user](/docs/registry-api.md#update-a-exists-user)
### Get a single user
```
GET /-/user/org.couchdb.user::username
```
#### Response
```json
HTTP/1.1 200 OK
ETag: "32-984ee97e01aea166dcab6d1517c730e3"
{
"_id": "org.couchdb.user:fengmk2",
"_rev": "32-984ee97e01aea166dcab6d1517c730e3",
"name": "fengmk2",
"email": "fengmk2@gmail.com",
"type": "user",
"roles": [],
"date": "2014-08-04T10:43:07.063Z",
"fullname": "fengmk2",
"avatar": "https://secure.gravatar.com/avatar/95b9d41231617a05ced5604d242c9670?s=50&d=retro",
"freenode": "",
"github": "fengmk2",
"homepage": "http://fengmk2.github.com",
"twitter": "fengmk2",
"avatarMedium": "https://secure.gravatar.com/avatar/95b9d41231617a05ced5604d242c9670?s=100&d=retro",
"avatarLarge": "https://secure.gravatar.com/avatar/95b9d41231617a05ced5604d242c9670?s=496&d=retro",
"fields": [
{
"name": "fullname",
"value": "fengmk2",
"title": "Full Name",
"show": "fengmk2"
},
{
"name": "email",
"value": "fengmk2@gmail.com",
"title": "Email",
"show": "<a href=\"mailto:fengmk2@gmail.com\">fengmk2@gmail.com</a>"
},
{
"name": "github",
"value": "fengmk2",
"title": "Github",
"show": "<a rel=\"me\" href=\"https://github.com/fengmk2\">fengmk2</a>"
},
{
"name": "twitter",
"value": "fengmk2",
"title": "Twitter",
"show": "<a rel=\"me\" href=\"https://twitter.com/fengmk2\">@fengmk2</a>"
},
{
"name": "appdotnet",
"value": "",
"title": "App.net",
"show": ""
},
{
"name": "homepage",
"value": "http://fengmk2.github.com",
"title": "Homepage",
"show": "<a rel=\"me\" href=\"http://fengmk2.github.com/\">http://fengmk2.github.com</a>"
},
{
"name": "freenode",
"value": "",
"title": "IRC Handle",
"show": ""
}
],
"appdotnet": "fengmk2"
}
```
### Add a new user
```
PUT /-/user/org.couchdb.user::username
```
#### Input
```json
{
"name": "admin",
"password": "123",
"email": "fengmk2@gmail.com",
"_id": "org.couchdb.user:admin",
"type": "user",
"roles": [],
"date": "2014-08-05T16:05:17.792Z"
}
```
#### Response
```json
Status: 201 Created
{
"ok": true,
"id": "org.couchdb.user:fengmk2",
"rev": "32-984ee97e01aea166dcab6d1517c730e3"
}
```
### Update a exists user
* Authentication required.
```
PUT /-/user/org.couchdb.user::username/-rev/:rev
```
#### Input
```json
{
"name": "admin",
"password": "123",
"email": "fengmk2@gmail.com",
"_id": "org.couchdb.user:admin",
"type": "user",
"roles": [],
"date": "2014-08-05T16:05:17.792Z",
"_rev": "2-1a18c3d73ba42e863523a399ff3304d8"
}
```
#### Response
```json
Status: 201 Created
{
"ok": true,
"id": "org.couchdb.user:fengmk2",
"rev": "3-bb300a90c9aeb779748b83ec1b744039"
}
```
## Search

View File

@@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS `npm_module_maintainer` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name',
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
PRIMARY KEY (`id`),
UNIQUE KEY `user_module_name` (`user`,`name`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='npm original module maintainers';

View File

@@ -6,7 +6,9 @@ So `cnpm` is meaning: **Company npm**.
* Our public registry: [r.cnpmjs.org](http://r.cnpmjs.org), syncing from [registry.npmjs.org](http://registry.npmjs.org)
* Current [cnpmjs.org](/) version: <span id="app-version"></span>
* Mirror of [nodejs.org/dist](http://nodejs.org/dist): http://cnpmjs.org/dist
* Mirror of [Node.js Manual & Documentation](/dist/latest/docs/api/index.html)
* Mirror of [nodejs.org/dist](http://nodejs.org/dist): [/dist mirror](/dist)
* Mirror of [phantomjs downloads](https://bitbucket.org/ariya/phantomjs/downloads): [phantomjs mirror](/dist/phantomjs/)
<table class="downloads">
<tbody>
@@ -103,6 +105,16 @@ $(function () {
});
</script>
## Version Badge
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)
* `<0.1.0 & >=0.0.0`: ![red-badge](https://img.shields.io/badge/cnpm-0.0.1-red.svg?style=flat-square)
* `<1.0.0 & >=0.1.0`: ![red-badge](https://img.shields.io/badge/cnpm-0.1.0-green.svg?style=flat-square)
* `>=1.0.0`: ![red-badge](https://img.shields.io/badge/cnpm-1.0.0-blue.svg?style=flat-square)
## 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:
@@ -176,8 +188,3 @@ Release [History](/history).
## npm and cnpm relation
![npm&cnpm](https://docs.google.com/drawings/d/12QeQfGalqjsB77mRnf5Iq5oSXHCIUTvZTwECMonqCmw/pub?w=383&h=284)
## 捐赠 Donate
如果您觉得 [cnpmjs.org](/) 对您有帮助,欢迎请作者一杯咖啡.
[![Donate](https://img.alipay.com/sys/personalprod/style/mc/btn-index.png)](https://me.alipay.com/imk2)

View File

@@ -15,46 +15,50 @@
*/
var debug = require('debug')('cnpmjs.org:middleware:auth');
var User = require('../proxy/user');
var config = require('../config');
var common = require('../lib/common');
var UserService = require('../services/user');
/**
* Parse the request authorization
* get the real user
*/
module.exports = function (options) {
return function *auth(next) {
var session = yield *this.session;
debug('%s, %s, %j', this.url, this.sessionId, session);
return function* auth(next) {
this.user = {};
if (session.name) {
this.user.name = session.name;
this.user.isAdmin = common.isAdmin(session.name);
debug('auth exists user: %j, headers: %j', this.user, this.header);
return yield *next;
}
var authorization = (this.get('authorization') || '').split(' ')[1] || '';
authorization = authorization.trim();
debug('%s %s with %j', this.method, this.url, authorization);
if (!authorization) {
return yield *next;
return yield* next;
}
authorization = new Buffer(authorization, 'base64').toString().split(':');
if (authorization.length !== 2) {
return yield *next;
return yield* next;
}
var username = authorization[0];
var password = authorization[1];
var row = yield User.auth(username, password);
if (!row) {
debug('auth fail user: %j, headers: %j', row, this.header);
return yield *next;
var row;
try {
row = yield* UserService.auth(username, password);
} catch (err) {
// do not response error here
// many request do not need login
this.user.error = err;
}
this.user.name = row.name;
this.user.isAdmin = common.isAdmin(row.name);
if (!row) {
debug('auth fail user: %j, headers: %j', row, this.header);
return yield* next;
}
this.user.name = row.login;
this.user.isAdmin = row.site_admin;
this.user.scopes = row.scopes;
debug('auth pass user: %j, headers: %j', this.user, this.header);
yield *next;
yield* next;
};
};

View File

@@ -14,8 +14,24 @@
* Module dependencies.
*/
var http = require('http');
module.exports = function *login(next) {
if (this.user.error) {
var status = this.user.error.status;
this.status = http.STATUS_CODES[status]
? status
: 500;
this.body = {
error: this.user.error.name,
reason: this.user.error.message
};
return;
}
if (!this.user.name) {
this.status = 401;
this.body = {
error: 'unauthorized',

View File

@@ -14,11 +14,14 @@
* Module dependencies.
*/
var util = require('util');
var config = require('../config');
var debug = require('debug')('cnpmjs.org:middlewares/publishable');
module.exports = function *publishable(next) {
// private mode, only admin user can publish
if (config.enablePrivate && !this.user.isAdmin) {
// private mode, only admin user can publish
this.status = 403;
this.body = {
error: 'no_perms',
@@ -26,5 +29,75 @@ module.exports = function *publishable(next) {
};
return;
}
yield *next;
// public mode, all user have permission to publish
// but if `config.scopes` exist, only can publish with scopes in `config.scope`
// if `config.forcePublishWithScope` set to true, only admins can publish without scope
var name = this.params.name || this.params[0];
// check if is private package list in config
if (config.privatePackages && config.privatePackages.indexOf(name) !== -1) {
return yield* next;
}
// scope
if (name[0] === '@') {
if (checkScope(name, this)) {
return yield* next;
}
return;
}
// none-scope
if (checkNoneScope(this)) {
return yield* next;
}
};
/**
* check module's scope legal
*/
function checkScope(name, ctx) {
if (!ctx.user.scopes || !ctx.user.scopes.length) {
ctx.status = 404;
return false;
}
var scope = name.split('/')[0];
if (ctx.user.scopes.indexOf(scope) === -1) {
debug('assert scope %s error', name);
ctx.status = 400;
ctx.body = {
error: 'invalid scope',
reason: util.format('scope %s not match legal scopes %j', scope, ctx.user.scopes)
};
return false;
}
return true;
}
/**
* check if user have permission to publish without scope
*/
function checkNoneScope(ctx) {
if (!config.scopes
|| !config.scopes.length
|| !config.forcePublishWithScope) {
return true;
}
// only admins can publish or unpublish non-scope modules
if (ctx.user.isAdmin) {
return true;
}
ctx.status = 403;
ctx.body = {
error: 'no_perms',
reason: 'only allow publish with ' + ctx.user.scopes.join(',') + ' scope(s)'
};
}

View File

@@ -20,6 +20,10 @@ module.exports = function *notFound(next) {
if (this.status && this.status !== 404) {
return;
}
if (this.body && this.body.name) {
return;
}
this.status = 404;
this.body = {
error: 'not_found',

View File

@@ -20,21 +20,22 @@ var config = require('../config');
* this.allowSync - allow sync triggle by cnpm install
*/
module.exports = function *syncByInstall(next) {
module.exports = function* syncByInstall(next) {
if (!config.syncByInstall || !config.enablePrivate) {
// only config.enablePrivate should enable sync on install
return yield *next;
return yield* next;
}
// request not by node, consider it request from web
var ua = this.get('user-agent');
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
if (this.query.write) {
return yield *next;
return yield* next;
}
this.allowSync = true;
yield *next;
yield* next;
};

View File

@@ -22,8 +22,15 @@ module.exports = function *notFound(next) {
if (this.status && this.status !== 404) {
return;
}
if (this.body) {
return;
}
var m = /^\/([\w\-\_\.]+)\/?$/.exec(this.url);
var m = /^\/([\w\-\.]+)\/?$/.exec(this.path);
if (!m) {
// scoped packages
m = /^\/(@[\w\-\.]+\/[\w\-\.]+)$/.exec(this.path);
}
debug('%s match %j', this.url, m);
if (m) {
return this.redirect('/package/' + m[1]);
@@ -31,16 +38,16 @@ module.exports = function *notFound(next) {
// package not found
m = /\/package\/([\w\-\_\.]+)\/?$/.exec(this.url);
var name = null;
var title = '404: Page Not Found';
if (m) {
var name = m[1];
this.status = 404;
yield* this.render('404', {
title: 'Package - ' + name,
name: name
});
return;
name = m[1];
title = name + ' Not Found';
}
this.status = 404;
this.body = 'Cannot GET ' + this.path;
yield* this.render('404', {
title: title,
name: name
});
};

View File

@@ -1,6 +1,6 @@
{
"name": "cnpmjs.org",
"version": "0.5.1",
"version": "1.7.3",
"description": "Private npm registry and web for Enterprise, base on MySQL and Simple Store Service",
"main": "index.js",
"scripts": {
@@ -10,54 +10,61 @@
"stop": "./bin/nodejsctl stop"
},
"dependencies": {
"bytes": "1.0.0",
"co": "3.0.6",
"co-defer": "0.1.0",
"co-gather": "0.0.1",
"co-read": "0.0.2",
"co-redis": "1.1.0",
"co-urllib": "0.2.2",
"co-write": "0.3.0",
"copy-to": "1.0.1",
"debug": "0.8.1",
"eventproxy": "0.3.1",
"giturl": "0.0.3",
"graceful": "0.0.6",
"gravatar": "1.0.6",
"humanize-number": "0.0.2",
"koa": "0.6.1",
"koa-limit": "1.0.2",
"koa-markdown": "0.0.3",
"koa-middlewares": "0.2.1",
"logfilestream": "0.1.0",
"marked": "0.3.2",
"mime": "1.2.11",
"mkdirp": "0.5.0",
"moment": "2.6.0",
"ms": "0.6.2",
"multiline": "0.3.4",
"mysql": "2.3.0",
"nodemailer": "0.6.5",
"qn": "0.2.2",
"ready": "0.1.1",
"redis": "0.10.3",
"semver": "2.3.0",
"thunkify-wrap": "0.1.2",
"utility": "0.1.13"
"agentkeepalive": "~1.2.0",
"bluebird": "~2.3.5",
"bytes": "~1.0.0",
"cfork": "~1.2.0",
"cheerio": "~0.17.0",
"co": "~3.1.0",
"co-defer": "~0.1.1",
"co-gather": "~0.0.1",
"co-read": "~0.1.0",
"co-redis": "~1.1.0",
"co-sleep": "~0.0.1",
"co-write": "~0.3.0",
"copy-to": "~1.0.1",
"debug": "~2.0.0",
"error-formater": "~1.0.3",
"eventproxy": "~0.3.1",
"fs-cnpm": "~1.1.0",
"giturl": "~0.0.3",
"graceful": "~0.1.0",
"gravatar": "~1.1.0",
"humanize-ms": "~1.0.0",
"humanize-number": "~0.0.2",
"koa": "~0.12.2",
"koa-limit": "~1.0.2",
"koa-markdown": "~0.0.3",
"koa-middlewares": "~1.4.1",
"marked": "~0.3.2",
"mime": "~1.2.11",
"mini-logger": "~1.0.0",
"mkdirp": "~0.5.0",
"moment": "~2.8.3",
"multiline": "~1.0.1",
"mysql": "~2.5.2",
"nodemailer": "~1.3.0",
"ready": "~0.1.1",
"redis": "~0.12.1",
"semver": "~4.0.3",
"thunkify-wrap": "~1.0.2",
"urllib": "~1.5.2",
"utility": "~1.2.0"
},
"devDependencies": {
"autod": "~0.2.0",
"chunkstream": "0.0.1",
"autod": "^1.0.0",
"chunkstream": "~0.0.1",
"co-mocha": "0.0.2",
"contributors": "*",
"cov": "*",
"istanbul-harmony": "*",
"jshint": "*",
"mm": "0.2.1",
"mocha": "*",
"pedding": "0.0.3",
"should": "3.3.2",
"supertest": "0.13.0"
"istanbul-harmony": "~0.3.0",
"jshint": "~2.5.6",
"mm": "~0.2.1",
"mocha": "~1.21.5",
"node-dev": "*",
"pedding": "~1.0.0",
"should": "~4.0.4",
"should-http": "0.0.1",
"supertest": "~0.14.0"
},
"homepage": "https://github.com/cnpm/cnpmjs.org",
"repository": {
@@ -77,11 +84,14 @@
"registry"
],
"engines": {
"node": ">= 0.11.9"
"node": ">= 0.11.14"
},
"author": [
"fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)",
"dead_horse <dead_horse@qq.com> (http://deadhorse.me)"
],
"publishConfig": {
"tag": "v1"
},
"license": "MIT"
}

View File

@@ -9,6 +9,7 @@
*/
'use strict';
/* jshint -W032 */
/**
* Module dependencies.

View File

@@ -9,6 +9,8 @@
*/
'use strict';
/* jshint -W032 */
/**
* Module dependencies.

View File

@@ -9,6 +9,7 @@
*/
'use strict';
/* jshint -W032 */
/**
* Module dependencies.
@@ -21,9 +22,6 @@ var config = require('../config');
var mysql = require('../common/mysql');
var multiline = require('multiline');
var MODULE_COLUMNS = 'id, publish_time, gmt_create, gmt_modified, author, name, \
version, description, package, dist_tarball, dist_shasum, dist_size';
var INSERT_MODULE_SQL = multiline(function () {;/*
INSERT INTO
module(gmt_create, gmt_modified, publish_time, author, name, version,
@@ -59,7 +57,9 @@ exports.add = function (mod, callback) {
dist.size = 0;
var publish_time = mod.publish_time || Date.now();
var values = [
publish_time, mod.author, mod.name, mod.version, pkg,
publish_time,
mod.author, // meaning first maintainer, more maintainers please check module_maintainer table
mod.name, mod.version, pkg,
dist.tarball, dist.shasum, dist.size, description
];
mysql.query(INSERT_MODULE_SQL, values, function (err, result) {
@@ -164,39 +164,7 @@ exports.updateDescription = function (id, description, callback) {
mysql.query(UPDATE_DESC_SQL, [description, id], callback);
};
var UPDATE_PACKAGE_SQL = multiline(function () {;/*
UPDATE
module
SET
package=?
WHERE
id=?;
*/});
exports.updateReadme = function (id, readme, callback) {
exports.getById(id, function (err, data) {
if (err) {
return callback(err);
}
data.package = data.package || {};
data.package.readme = readme;
var pkg = stringifyPackage(data.package);
mysql.query(UPDATE_PACKAGE_SQL, [pkg, id], callback);
});
};
var UPDATE_DIST_SQL = multiline(function () {;/*
UPDATE
module
SET
publish_time=?,
version=?,
package=?,
dist_tarball=?,
dist_shasum=?,
dist_size=?
WHERE
id=?;
*/});
var UPDATE_DIST_SQL = 'UPDATE module SET ? WHERE id=?';
exports.update = function (mod, callback) {
var pkg;
try {
@@ -205,8 +173,18 @@ exports.update = function (mod, callback) {
return callback(e);
}
var dist = mod.package.dist;
var arg = {
publish_time: mod.publish_time,
version: mod.version,
package: pkg,
dist_tarball: dist.tarball,
dist_shasum: dist.shasum,
dist_size: dist.size
};
mysql.query(UPDATE_DIST_SQL,
[mod.publish_time, mod.version, pkg, dist.tarball, dist.shasum, dist.size, mod.id],
[arg, mod.id],
function (err, result) {
if (err) {
return callback(err);
@@ -350,7 +328,7 @@ var DELETE_TAGS_BY_IDS_SQL = multiline(function () {;/*
DELETE FROM
tag
WHERE
id in (?);
id IN (?);
*/});
exports.removeTagsByIds = function (ids, callback) {
mysql.query(DELETE_TAGS_BY_IDS_SQL, [ids], callback);
@@ -499,70 +477,13 @@ exports.removeByNameAndVersions = function (name, versions, callback) {
mysql.query(DELETE_MODULE_BY_NAME_AND_VERSIONS_SQL, [name, versions], callback);
};
var LIST_BY_AUTH_SQLS = [];
LIST_BY_AUTH_SQLS.push(multiline(function () {;/*
SELECT
distinct(name) AS name
FROM
module
WHERE
author=?
ORDER BY
publish_time DESC
LIMIT
100;
*/}));
LIST_BY_AUTH_SQLS.push(multiline(function () {;/*
SELECT
module_id
FROM
tag
WHERE
tag="latest" AND name IN (?);
*/}));
LIST_BY_AUTH_SQLS.push(multiline(function () {;/*
SELECT
name, description
FROM
module
WHERE
id IN (?)
ORDER BY
publish_time DESC;
*/}));
exports.listByAuthor = function (author, callback) {
var ep = eventproxy.create();
ep.fail(callback);
mysql.query(LIST_BY_AUTH_SQLS[0], [author], ep.done(function (rows) {
if (!rows || rows.length === 0) {
return callback(null, []);
}
ep.emit('names', rows.map(function (r) {
return r.name;
}));
}));
ep.on('names', function (names) {
mysql.query(LIST_BY_AUTH_SQLS[1], [names], ep.done(function (rows) {
if (!rows || rows.length === 0) {
return callback(null, []);
}
ep.emit('ids', rows.map(function (r) {
return r.module_id;
}));
}));
});
ep.on('ids', function (ids) {
mysql.query(LIST_BY_AUTH_SQLS[2], [ids], callback);
});
};
var SEARCH_MODULES_SQL = multiline(function () {;/*
SELECT
module_id
FROM
tag
WHERE
name LIKE ? AND tag="latest"
LOWER(name) LIKE LOWER(?) AND tag="latest"
ORDER BY
name
LIMIT
@@ -653,13 +574,6 @@ exports.search = function (word, options, callback) {
thunkify(exports);
exports.updateMaintainers = function *(id, maintainers) {
var mod = yield exports.getById(id);
mod.package.maintainers = maintainers;
var pkg = stringifyPackage(mod.package);
return yield mysql.query(UPDATE_PACKAGE_SQL, [pkg, id]);
};
var GET_LAST_MODIFIED_MODULE_SQL = multiline(function () {;/*
SELECT
id, gmt_modified
@@ -668,9 +582,197 @@ var GET_LAST_MODIFIED_MODULE_SQL = multiline(function () {;/*
WHERE
name=?
ORDER BY
gmt_modified DESC;
gmt_modified DESC
LIMIT 1;
*/});
exports.getLastModified = function *(name) {
exports.getLastModified = function* (name) {
var row = yield mysql.queryOne(GET_LAST_MODIFIED_MODULE_SQL, [name]);
return row && row.gmt_modified;
};
var UPDATE_LAST_MODIFIED_SQL = 'UPDATE module SET gmt_modified=now() WHERE id=?;';
exports.updateLastModified = function* (name) {
var row = yield mysql.queryOne(GET_LAST_MODIFIED_MODULE_SQL, [name]);
if (row) {
yield mysql.query(UPDATE_LAST_MODIFIED_SQL, [row.id]);
}
};
var DELETE_TAGS_BY_NAMES_SQL = 'DELETE FROM tag WHERE name=? AND tag IN (?);';
exports.removeTagsByNames = function* (moduleName, tagNames) {
return yield mysql.query(DELETE_TAGS_BY_NAMES_SQL, [moduleName, tagNames]);
};
/**
* forward compatbility for update from lower version cnpmjs.org
* redirect @scope/name => name
*/
exports.getAdaptName = function* (name) {
if (!config.scopes
|| !config.scopes.length
|| !config.adaptScope) {
return;
}
var tmp = name.split('/');
var scope = tmp[0];
name = tmp[1];
if (config.scopes.indexOf(scope) === -1) {
return;
}
var pkg = yield exports.getByTag(name, 'latest');
// only private module can adapt
if (pkg && pkg.package._publish_on_cnpm) {
return name;
}
return;
};
exports.listPrivates = function* () {
var scopes = config.scopes;
if (!scopes || !scopes.length) {
return [];
}
var privatePackages = config.privatePackages || [];
var args = [];
var sql = 'SELECT module_id AS id FROM tag WHERE tag="latest" AND (';
var wheres = [];
scopes.forEach(function (scope) {
wheres.push('name LIKE ?');
args.push(scope + '%');
});
if (privatePackages.length) {
wheres.push('name in (?)');
args.push(privatePackages);
}
sql = sql + wheres.join(' OR ') + ')';
var ids = yield mysql.query(sql, args);
ids = ids.map(function (row) {
return row.id;
});
if (!ids.length) {
return [];
}
return yield mysql.query(QUERY_MODULES_BY_ID_SQL, [ids]);
};
var LIST_BY_AUTH_SQLS = [];
LIST_BY_AUTH_SQLS.push(multiline(function () {;/*
SELECT
distinct(name) AS name
FROM
module
WHERE
author=?
ORDER BY
publish_time DESC
LIMIT
100;
*/}));
LIST_BY_AUTH_SQLS.push(multiline(function () {;/*
SELECT
name
FROM
module_maintainer
WHERE
user = ?
*/}));
LIST_BY_AUTH_SQLS.push(multiline(function () {;/*
SELECT
module_id
FROM
tag
WHERE
tag="latest" AND name IN (?);
*/}));
LIST_BY_AUTH_SQLS.push(multiline(function () {;/*
SELECT
name, description
FROM
module
WHERE
id IN (?)
ORDER BY
publish_time DESC;
*/}));
exports.listByAuthor = function* (author) {
var names = yield [
mysql.query(LIST_BY_AUTH_SQLS[0], [author]),
mysql.query(LIST_BY_AUTH_SQLS[1], [author])
];
names = names[0].concat(names[1]).map(function (n) {
return n.name;
}).sort();
if (!names.length) {
return [];
}
var ids = yield mysql.query(LIST_BY_AUTH_SQLS[2], [names]);
if (!ids.length) {
return [];
}
ids = ids.map(function (i) {
return i.module_id;
});
return yield mysql.query(LIST_BY_AUTH_SQLS[3], [ids]);
};
exports.listNamesByAuthor = function* (author) {
var sql = 'SELECT distinct(name) AS name FROM module WHERE author=?;';
var names = yield mysql.query(sql, [author]);
return names.map(function (item) {
return item.name;
});
};
var UPDATE_PACKAGE_SQL = multiline(function () {;/*
UPDATE
module
SET
package=?
WHERE
id=?;
*/});
exports.updatePackage = function* (id, pkg) {
pkg = stringifyPackage(pkg);
return yield mysql.query(UPDATE_PACKAGE_SQL, [pkg, id]);
};
exports.updatePackageFields = function* (id, fields) {
var data = yield exports.getById(id);
if (!data) {
throw new Error('module#' + id + ' not exists');
}
data.package = data.package || {};
for (var k in fields) {
data.package[k] = fields[k];
}
return yield* exports.updatePackage(id, data.package);
};
exports.updateReadme = function* (id, readme) {
var data = yield exports.getById(id);
if (!data) {
throw new Error('module#' + id + ' not exists');
}
data.package = data.package || {};
data.package.readme = readme;
return yield* exports.updatePackage(id, data.package);
};
exports.getTag = function* (name, tag) {
return yield mysql.queryOne(SELECT_TAG_SQL, [name, tag]);
};

View File

@@ -9,6 +9,7 @@
*/
"use strict";
/* jshint -W032 */
/**
* Module dependencies.

View File

@@ -9,6 +9,7 @@
*/
'use strict';
/* jshint -W032 */
/**
* Module dependencies.
@@ -16,16 +17,18 @@
var thunkify = require('thunkify-wrap');
var mysql = require('../common/mysql');
var multiline = require('multiline');
var INSERT_LOG_SQL = multiline(function () {;/*
INSERT INTO
module_log(gmt_create, gmt_modified, name, username, log)
VALUES
(now(), now(), ?, ?, "");
*/});
var INSERT_LOG_SQL = 'INSERT INTO module_log SET ?';
exports.create = function (data, callback) {
mysql.query(INSERT_LOG_SQL, [data.name, data.username], function (err, result) {
var now = new Date();
var args = {
gmt_create: now,
gmt_modified: now,
name: data.name,
username: data.username,
log: ''
};
mysql.query(INSERT_LOG_SQL, [args], function (err, result) {
if (err) {
return callback(err);
}
@@ -33,15 +36,7 @@ exports.create = function (data, callback) {
});
};
var APPEND_SQL = multiline(function () {;/*
UPDATE
module_log
SET
log=CONCAT(log, ?),
gmt_modified=now()
WHERE
id=?;
*/});
var APPEND_SQL = 'UPDATE module_log SET log=CONCAT(log, ?), gmt_modified=now() WHERE id=?';
exports.append = function (id, log, callback) {
log = '\n' + log;
mysql.query(APPEND_SQL, [log, id], function (err) {
@@ -49,14 +44,7 @@ exports.append = function (id, log, callback) {
});
};
var SELECT_SQL = multiline(function () {;/*
SELECT
*
FROM
module_log
WHERE
id=?;
*/});
var SELECT_SQL = 'SELECT * FROM module_log WHERE id=?';
exports.get = function (id, callback) {
mysql.queryOne(SELECT_SQL, [id], callback);
};

View File

@@ -0,0 +1,92 @@
/**!
* cnpmjs.org - proxy/module_maintainer.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:proxy:module_maintainer');
var mysql = require('../common/mysql');
var Module = require('./module');
var GET_MAINTANINERS_SQL = 'SELECT user FROM module_maintainer WHERE name = ?;';
exports.get = function* (name) {
var users = yield mysql.query(GET_MAINTANINERS_SQL, [name]);
return users.map(function (row) {
return row.user;
});
};
var ADD_SQL = 'INSERT INTO module_maintainer(name, user, gmt_create) \
VALUES (?, ?, now());';
function* add(name, username) {
try {
yield mysql.query(ADD_SQL, [name, username]);
} catch (err) {
if (err.code !== 'ER_DUP_ENTRY') {
throw err;
}
}
}
var REMOVE_SQL = 'DELETE FROM module_maintainer WHERE name = ? AND user IN (?);';
function* remove(name, usernames) {
return yield mysql.query(REMOVE_SQL, [name, usernames]);
}
var REMOVE_ALL_SQL = 'DELETE FROM module_maintainer WHERE name = ?';
exports.removeAll = function* (name) {
return yield mysql.query(REMOVE_ALL_SQL, [name]);
};
exports.addMulti = function* (name, usernames) {
var tasks = [];
for (var i = 0; i < usernames.length; i++) {
tasks.push(add(name, usernames[i]));
}
return yield tasks;
};
exports.update = function* (name, maintainers) {
// maintainers should be [name1, name2, ...] format
// find out the exists maintainers then remove the deletes and add the left
if (maintainers.length === 0) {
return {
add: [],
remove: []
};
}
var exists = yield* exports.get(name);
var addUsers = maintainers;
var removeUsers = [];
if (exists.length > 0) {
for (var i = 0; i < exists.length; i++) {
var username = exists[i];
if (addUsers.indexOf(username) === -1) {
removeUsers.push(username);
}
}
}
yield* exports.addMulti(name, addUsers);
// make sure all add users success then remove users
if (removeUsers.length > 0) {
yield* remove(name, removeUsers);
}
debug('add %d users, remove %d users', addUsers.length, removeUsers.length);
return {
add: addUsers,
remove: removeUsers
};
};

View File

@@ -9,6 +9,7 @@
*/
'use strict';
/* jshint -W032 */
/**
* Module dependencies.
@@ -18,10 +19,10 @@ var mysql = require('../common/mysql');
var multiline = require('multiline');
var ADD_SQL = multiline(function () {;/*
INSERT iNTO
module_star(name, user)
INSERT INTO
module_star(name, user, gmt_create)
VALUES
(?, ?);
(?, ?, now());
*/});
exports.add = function *add(name, user) {
try {

View File

@@ -0,0 +1,44 @@
/**!
* cnpmjs.org - proxy/module_unpublished.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/* jshint -W032 */
/**
* Module dependencies.
*/
var multiline = require('multiline');
var mysql = require('../common/mysql');
var SAVE_SQL = multiline(function () {;/*
INSERT INTO
module_unpublished(gmt_create, gmt_modified, name, package)
VALUES
(now(), now(), ?, ?)
ON DUPLICATE KEY UPDATE
gmt_modified=now(),
name=VALUES(name),
package=VALUES(package);
*/});
exports.add = function* (name, pkg) {
return yield mysql.query(SAVE_SQL, [name, JSON.stringify(pkg)]);
};
var GET_SQL = 'SELECT gmt_modified, name, package FROM module_unpublished WHERE name=?;';
exports.get = function* (name) {
var row = yield mysql.queryOne(GET_SQL, [name]);
if (row) {
row.package = JSON.parse(row.package);
}
return row;
};

View File

@@ -14,12 +14,12 @@
* Module dependencies.
*/
var urllib = require('co-urllib');
var urllib = require('../common/urllib');
var config = require('../config');
var USER_AGENT = 'cnpmjs.org/' + config.version + ' ' + urllib.USER_AGENT;
function *request(url, options) {
function* request(url, options) {
options = options || {};
options.dataType = options.dataType || 'json';
options.timeout = options.timeout || 120000;
@@ -30,7 +30,7 @@ function *request(url, options) {
url = registry + url;
var r;
try {
r = yield *urllib.request(url, options);
r = yield urllib.request(url, options);
} catch (err) {
var statusCode = err.status || -1;
var data = err.data || '[empty]';
@@ -43,6 +43,8 @@ function *request(url, options) {
return r;
}
exports.request = request;
exports.getUser = function *(name) {
var url = '/-/user/org.couchdb.user:' + name;
var r = yield *request(url);
@@ -62,17 +64,39 @@ exports.get = function *(name) {
return data;
};
exports.getAllSince = function *(startkey) {
exports.getAllSince = function *(startkey, timeout) {
var r = yield *request('/-/all/since?stale=update_after&startkey=' + startkey, {
timeout: 300000
timeout: timeout || 300000
});
return r.data;
};
exports.getShort = function *() {
exports.getShort = function *(timeout) {
var r = yield *request('/-/short', {
timeout: 300000,
registry: 'http://r.cnpmjs.org', // registry.npmjs.org/-/short is 404 now.
timeout: timeout || 300000,
// registry.npmjs.org/-/short is 404 now therefore have a fallback
registry: config.sourceNpmRegistryIsCNpm ? config.sourceNpmRegistry : 'http://r.cnpmjs.org',
});
return r.data;
};
exports.getPopular = function* (top, timeout) {
var r = yield *request('/-/_view/dependedUpon?group_level=1', {
registry: config.officialNpmRegistry,
timeout: timeout || 60000
});
if (!r.data || !r.data.rows || !r.data.rows.length) {
return [];
}
return r.data.rows.sort(function (a, b) {
return b.value - a.value;
})
.slice(0, top)
.map(function (r) {
return r.key && r.key[0]
})
.filter(function (r) {
return r;
});
}

View File

@@ -0,0 +1,54 @@
/**!
* cnpmjs.org - proxy/npm_module_maintainer.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var mysql = require('../common/mysql');
exports.add = function* (name, username) {
var sql = 'INSERT INTO npm_module_maintainer(name, user, gmt_create) \
VALUES (?, ?, now());';
try {
yield mysql.query(sql, [name, username]);
} catch (err) {
if (err.code !== 'ER_DUP_ENTRY') {
throw err;
}
}
};
exports.remove = function* (name, username) {
var sql = 'DELETE FROM npm_module_maintainer WHERE name = ? AND user = ?;';
return yield mysql.query(sql, [name, username]);
};
exports.removeAll = function* (name) {
var sql = 'DELETE FROM npm_module_maintainer WHERE name = ?;';
return yield mysql.query(sql, [name]);
};
exports.list = function* (name) {
var sql = 'SELECT user FROM npm_module_maintainer WHERE name = ?;';
return yield mysql.query(sql, [name]);
};
exports.listByUsers = function* (users) {
var sql = 'SELECT name, user FROM npm_module_maintainer WHERE user = ?;';
var args = users;
if (users.length > 1) {
sql = 'SELECT name, user FROM npm_module_maintainer WHERE user in (?);';
args = [users];
}
return yield mysql.query(sql, args);
};

View File

@@ -25,9 +25,10 @@ var util = require('util');
var fs = require('fs');
var path = require('path');
var crypto = require('crypto');
var urllib = require('co-urllib');
var sleep = require('co-sleep');
var urllib = require('../common/urllib');
var utility = require('utility');
var ms = require('ms');
var urlparse = require('url').parse;
var nfs = require('../common/nfs');
var npm = require('./npm');
var common = require('../lib/common');
@@ -37,6 +38,8 @@ var Log = require('./module_log');
var config = require('../config');
var ModuleStar = require('./module_star');
var User = require('./user');
var ModuleUnpublished = require('./module_unpublished');
var NpmModuleMaintainer = require('./npm_module_maintainer');
var USER_AGENT = 'sync.cnpmjs.org/' + config.version + ' ' + urllib.USER_AGENT;
@@ -60,6 +63,7 @@ function SyncModuleWorker(options) {
this.nameMap[name] = true;
}.bind(this));
this.noDep = options.noDep; //do not sync dependences
this.syncDevDependencies = config.syncDevDependencies;
this.successes = [];
this.fails = [];
@@ -74,12 +78,14 @@ SyncModuleWorker.prototype.finish = function () {
if (this._finished || Object.keys(this.syncingNames).length > 0) {
return;
}
this._finished = true;
this.log('[done] Sync %s module finished, %d success, %d fail\nSuccess: [ %s ]\nFail: [ %s ]',
this.startName,
this.successes.length, this.fails.length,
this.successes.join(', '), this.fails.join(', '));
this.emit('end');
this._finished = true;
// make sure all event listeners release
this.removeAllListeners();
};
SyncModuleWorker.prototype.log = function (format, arg1, arg2) {
@@ -91,11 +97,16 @@ SyncModuleWorker.prototype.log = function (format, arg1, arg2) {
SyncModuleWorker.prototype.start = function () {
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);
var self = this;
var that = this;
co(function *() {
// sync upstream
if (config.sourceNpmRegistryIsCNpm) {
yield* that.syncUpstream(that.startName);
}
var arr = [];
for (var i = 0; i < self.concurrency; i++) {
arr.push(self.next(i));
for (var i = 0; i < that.concurrency; i++) {
arr.push(that.next(i));
}
yield arr;
})();
@@ -121,7 +132,9 @@ SyncModuleWorker.prototype.add = function (name) {
this.log(' add dependencies: %s', name);
};
SyncModuleWorker.prototype._doneOne = function *(concurrencyId, name, success) {
SyncModuleWorker.prototype._doneOne = function* (concurrencyId, name, success) {
this.log('----------------- Synced %s %s -------------------',
name, success ? 'success' : 'fail');
if (success) {
this.pushSuccess(name);
} else {
@@ -131,11 +144,77 @@ SyncModuleWorker.prototype._doneOne = function *(concurrencyId, name, success) {
var that = this;
// relase the stack: https://github.com/cnpm/cnpmjs.org/issues/328
defer.setImmediate(function *() {
that.log('[c#%s] setImmediate after, %s done, start next...', concurrencyId, name);
yield *that.next(concurrencyId);
});
};
SyncModuleWorker.prototype.syncUpstream = function* (name) {
var url = config.sourceNpmRegistry + '/' + name + '/sync';
if (this.noDep) {
url += '?nodeps=true';
}
var r = yield urllib.request(url, {
method: 'put',
timeout: 20000,
headers: {
'content-length': 0
},
dataType: 'json'
});
if (r.status !== 201 || !r.data.ok) {
return this.log('sync upstream %s error, status: %s, response: %j',
url, r.status, r.data);
}
var logURL = config.sourceNpmRegistry + '/' + name + '/sync/log/' + r.data.logId;
var offset = 0;
this.log('----------------- Syncing upstream %s -------------------', logURL);
var count = 0;
while (true) {
count++;
var synclogURL = logURL + '?offset=' + offset;
var rs = yield urllib.request(synclogURL, {
timeout: 20000,
dataType: 'json'
});
if (rs.status !== 200 || !rs.data.ok) {
this.log('sync upstream %s error, status: %s, response: %j',
synclogURL, rs.status, rs.data);
break;
}
var data = rs.data;
var syncDone = false;
if (data.log && data.log.indexOf('[done] Sync') >= 0) {
syncDone = true;
data.log = data.log.replace('[done] Sync', '[Upstream done] Sync');
}
if (data.log) {
this.log(data.log);
}
if (syncDone) {
break;
}
if (count >= 30) {
this.log('sync upstream %s fail, give up', logURL);
break;
}
if (data.log) {
offset += data.log.split('\n').length;
}
yield sleep(2000);
}
this.log('----------------- Synced upstream %s -------------------', logURL);
};
SyncModuleWorker.prototype.next = function *(concurrencyId) {
var name = this.names.shift();
if (!name) {
@@ -144,39 +223,69 @@ SyncModuleWorker.prototype.next = function *(concurrencyId) {
var that = this;
that.syncingNames[name] = true;
var pkg;
var pkg = null;
var status = 0;
this.log('----------------- Syncing %s -------------------', name);
// get from npm
try {
pkg = yield npm.get(name);
var result = yield npm.request('/' + name);
pkg = result.data;
status = result.status;
} catch (err) {
// if 404
if (!err.res || err.res.statusCode !== 404) {
var errMessage = err.name + ': ' + err.message;
that.log('[c#%s] [error] [%s] get package error: %s', concurrencyId, name, errMessage);
that.log('[c#%s] [error] [%s] get package error: %s, status: %s',
concurrencyId, name, errMessage, status);
yield *that._doneOne(concurrencyId, name, false);
return;
}
}
var unpublishedInfo = null;
if (status === 404) {
// check if it's unpublished
if (pkg.time && pkg.time.unpublished && pkg.time.unpublished.time) {
unpublishedInfo = pkg.time.unpublished;
} else {
pkg = null;
}
}
if (!pkg) {
that.log('[c#%s] [error] [%s] get package error: package not exists', concurrencyId, name);
yield *that._doneOne(concurrencyId, name, true);
that.log('[c#%s] [error] [%s] get package error: package not exists, status: %s',
concurrencyId, name, status);
yield* that._doneOne(concurrencyId, name, true);
return;
}
that.log('[c#%d] [%s] start...', concurrencyId, name);
that.log('[c#%d] [%s] pkg status: %d, start...', concurrencyId, name, status);
if (unpublishedInfo) {
try {
yield* that._unpublished(name, unpublishedInfo);
} catch (err) {
that.log('[c#%s] [error] [%s] sync error: %s', concurrencyId, name, err.stack);
yield* that._doneOne(concurrencyId, name, false);
return;
}
return yield* that._doneOne(concurrencyId, name, true);
}
var versions;
try {
versions = yield that._sync(name, pkg);
versions = yield* that._sync(name, pkg);
} catch (err) {
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;
}
that.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(', '));
yield *that._doneOne(concurrencyId, name, true);
yield* this._doneOne(concurrencyId, name, true);
};
function *_listStarUsers(modName) {
@@ -200,7 +309,69 @@ function *_saveNpmUser(username) {
yield User.saveNpmUser(user);
}
SyncModuleWorker.prototype._sync = function *(name, pkg) {
function *_saveMaintainer(modName, username, action) {
if (action === 'add') {
yield* NpmModuleMaintainer.add(modName, username);
} else if (action === 'remove') {
yield* NpmModuleMaintainer.remove(modName, username);
}
}
SyncModuleWorker.prototype._unpublished = function* (name, unpublishedInfo) {
var mods = yield Module.listByName(name);
this.log(' [%s] start unpublished %d versions from local cnpm registry',
name, mods.length);
if (this._isLocalModule(mods)) {
// publish on cnpm, dont sync this version package
this.log(' [%s] publish on local cnpm registry, don\'t sync', name);
return [];
}
var r = yield* ModuleUnpublished.add(name, unpublishedInfo);
this.log(' [%s] save unpublished info: %j to row#%s',
name, unpublishedInfo, r.insertId);
if (mods.length === 0) {
return;
}
yield [Module.removeByName(name), Module.removeTags(name)];
var keys = [];
for (var i = 0; i < mods.length; i++) {
var row = mods[i];
var dist = row.package.dist;
var key = dist.key;
if (!key) {
key = urlparse(dist.tarball).pathname;
}
key && keys.push(key);
}
if (keys.length === 0) {
return;
}
try {
yield keys.map(function (key) {
return nfs.remove(key);
});
} catch (err) {
// ignore error here
this.log(' [%s] delete nfs files: %j error: %s: %s',
name, keys, err.name, err.message);
}
this.log(' [%s] delete nfs files: %j success', name, keys);
};
SyncModuleWorker.prototype._isLocalModule = function (mods) {
for (var i = 0; i < mods.length; i++) {
var r = mods[i];
if (r.package && r.package._publish_on_cnpm) {
return true;
}
}
return false;
};
SyncModuleWorker.prototype._sync = function* (name, pkg) {
var username = this.username;
var that = this;
@@ -208,14 +379,23 @@ SyncModuleWorker.prototype._sync = function *(name, pkg) {
var result = yield [
Module.listByName(name),
Module.listTags(name),
_listStarUsers(name)
_listStarUsers(name),
NpmModuleMaintainer.list(name),
];
var moduleRows = result[0];
var tagRows = result[1];
var existsStarUsers = result[2];
var existsNpmMaintainers = result[3];
if (that._isLocalModule(moduleRows)) {
// publish on cnpm, dont sync this version package
that.log(' [%s] publish on local cnpm registry, don\'t sync', name);
return [];
}
hasModules = moduleRows.length > 0;
var map = {};
var localVersionNames = [];
for (var i = 0; i < moduleRows.length; i++) {
var r = moduleRows[i];
if (!r.package || !r.package.dist) {
@@ -223,12 +403,6 @@ SyncModuleWorker.prototype._sync = function *(name, pkg) {
continue;
}
if (r.package && r.package._publish_on_cnpm) {
// publish on cnpm, dont sync this version package
that.log(' [%s] publish on local cnpm, don\'t sync', name);
return [];
}
if (r.version === 'next') {
continue;
}
@@ -236,6 +410,7 @@ SyncModuleWorker.prototype._sync = function *(name, pkg) {
map.latest = r;
}
map[r.version] = r;
localVersionNames.push(r.version);
}
var tags = {};
@@ -254,6 +429,40 @@ SyncModuleWorker.prototype._sync = function *(name, pkg) {
var missingReadmes = [];
var missingStarUsers = [];
var npmUsernames = {};
var missingDeprecateds = [];
// [[user, 'add or remove'], ...]
var diffNpmMaintainers = [];
// find out new maintainers
var pkgMaintainers = pkg.maintainers || [];
if (Array.isArray(pkgMaintainers)) {
var existsMap = {};
var originalMap = {};
for (var i = 0; i < existsNpmMaintainers.length; i++) {
var item = existsNpmMaintainers[i];
existsMap[item.user] = item;
}
for (var i = 0; i < pkgMaintainers.length; i++) {
var item = pkgMaintainers[i];
originalMap[item.name] = item;
}
// find add users
for (var i = 0; i < pkgMaintainers.length; i++) {
var item = pkgMaintainers[i];
if (!existsMap[item.name]) {
diffNpmMaintainers.push([item.name, 'add']);
}
}
// find remove users
for (var i = 0; i < existsNpmMaintainers.length; i++) {
var item = existsNpmMaintainers[i];
if (!originalMap[item.user]) {
diffNpmMaintainers.push([item.user, 'remove']);
}
}
}
// find out all user names
for (var v in pkg.versions) {
@@ -262,17 +471,17 @@ SyncModuleWorker.prototype._sync = function *(name, pkg) {
var maintainers = p.maintainers || [];
if (maintainers && !Array.isArray(maintainers)) {
// http://r.cnpmjs.org/jasmine-node
// TODO: "maintainers": "Martin Häger <martin.haeger@gmail.com>",
// TODO: "maintainers": "Martin H膫陇ger <martin.haeger@gmail.com>",
maintainers = [maintainers];
}
maintainers.forEach(function (m) {
if (m.name) {
npmUsernames[m.name.toLowerCase()] = 1;
}
});
maintainers.forEach(pushName);
}
function pushName(m) {
if (m.name) {
npmUsernames[m.name.toLowerCase()] = 1;
}
}
// get the missing star users
var starUsers = pkg.users || {};
for (var k in starUsers) {
@@ -285,11 +494,8 @@ SyncModuleWorker.prototype._sync = function *(name, pkg) {
var times = pkg.time || {};
pkg.versions = pkg.versions || {};
var versionNames = Object.keys(times);
if (versionNames.length === 0) {
versionNames = Object.keys(pkg.versions);
}
if (versionNames.length === 0) {
var remoteVersionNames = Object.keys(pkg.versions);
if (remoteVersionNames.length === 0) {
that.log(' [%s] no times and no versions, hasModules: %s', name, hasModules);
if (!hasModules) {
// save a next module
@@ -323,9 +529,12 @@ SyncModuleWorker.prototype._sync = function *(name, pkg) {
}
}
var versions = [];
for (var i = 0; i < versionNames.length; i++) {
var v = versionNames[i];
var remoteVersionNameMap = {};
// find out missing versions
for (var i = 0; i < remoteVersionNames.length; i++) {
var v = remoteVersionNames[i];
remoteVersionNameMap[v] = v;
var exists = map[v] || {};
var version = pkg.versions[v];
if (!version || !version.dist || !version.dist.tarball) {
@@ -340,12 +549,8 @@ SyncModuleWorker.prototype._sync = function *(name, pkg) {
if (!version.maintainers || !version.maintainers[0]) {
version.maintainers = pkg.maintainers;
}
var sourceAuthor = version.maintainers && version.maintainers[0] &&
version.maintainers[0].name || exists.author;
if (exists.package && exists.package.dist.shasum === version.dist.shasum &&
exists.author === sourceAuthor) {
// * author make sure equal
if (exists.package &&
exists.package.dist.shasum === version.dist.shasum) {
// * shasum make sure equal
if ((version.publish_time === exists.publish_time) ||
(!version.publish_time && exists.publish_time)) {
@@ -367,12 +572,30 @@ SyncModuleWorker.prototype._sync = function *(name, pkg) {
readme: version.readme
});
}
if (version.deprecated && version.deprecated !== exists.package.deprecated) {
// need to sync deprecated field
missingDeprecateds.push({
id: exists.id,
deprecated: version.deprecated
});
}
continue;
}
}
versions.push(version);
missingVersions.push(version);
}
// find out deleted versions
var deletedVersionNames = [];
for (var i = 0; i < localVersionNames.length; i++) {
var v = localVersionNames[i];
if (!remoteVersionNameMap[v]) {
deletedVersionNames.push(v);
}
}
// find out missing tags
var sourceTags = pkg['dist-tags'] || {};
for (var t in sourceTags) {
var sourceTagVersion = sourceTags[t];
@@ -380,18 +603,25 @@ SyncModuleWorker.prototype._sync = function *(name, pkg) {
missingTags.push([t, sourceTagVersion]);
}
}
if (versions.length === 0) {
that.log(' [%s] all versions are exists', name);
} else {
versions.sort(function (a, b) {
return a.publish_time - b.publish_time;
});
that.log(' [%s] %d versions need to sync', name, versions.length);
// find out deleted tags
var deletedTags = [];
for (var t in tags) {
if (!sourceTags[t]) {
// not in remote tags, delete it from local registry
deletedTags.push(t);
}
}
missingVersions = versions;
var versionNames = [];
if (missingVersions.length === 0) {
that.log(' [%s] all versions are exists', name);
} else {
missingVersions.sort(function (a, b) {
return a.publish_time - b.publish_time;
});
that.log(' [%s] %d versions need to sync', name, missingVersions.length);
}
var syncedVersionNames = [];
var syncIndex = 0;
// sync missing versions
@@ -403,10 +633,24 @@ SyncModuleWorker.prototype._sync = function *(name, pkg) {
}
try {
var result = yield that._syncOneVersion(index, syncModule);
versionNames.push(syncModule.version);
syncedVersionNames.push(syncModule.version);
} catch (err) {
that.log(' [%s:%d] error, version: %s, %s: %s',
syncModule.name, index, syncModule.version, err.name, err.message);
that.log(' [%s:%d] sync error, version: %s, %s: %s',
syncModule.name, index, syncModule.version, err.name, err.stack);
}
}
if (deletedVersionNames.length === 0) {
that.log(' [%s] no versions need to deleted', name);
} else {
that.log(' [%s] %d versions: %j need to deleted',
name, deletedVersionNames.length, deletedVersionNames);
try {
yield Module.removeByNameAndVersions(name, deletedVersionNames);
} catch (err) {
that.log(' [%s] delete error, %s: %s', name, err.name, err.message);
}
}
@@ -434,7 +678,13 @@ SyncModuleWorker.prototype._sync = function *(name, pkg) {
}
// sync missing tags
function *syncTag() {
function* syncTag() {
if (deletedTags.length > 0) {
yield* Module.removeTagsByNames(name, deletedTags);
that.log(' [%s] deleted %d tags: %j',
name, deletedTags.length, deletedTags);
}
if (missingTags.length === 0) {
return;
}
@@ -480,6 +730,29 @@ SyncModuleWorker.prototype._sync = function *(name, pkg) {
}
}
function *syncDeprecateds() {
if (missingDeprecateds.length === 0) {
return;
}
that.log(' [%s] saving %d Deprecated fields', name, missingDeprecateds.length);
var res = yield gather(missingDeprecateds.map(function (item) {
return Module.updatePackageFields(item.id, {
deprecated: item.deprecated
});
}));
for (var i = 0; i < res.length; i++) {
var item = missingDeprecateds[i];
var r = res[i];
if (r.error) {
that.log(' save error, id: %s, error: %s', item.id, r.error.message);
} else {
that.log(' saved, id: %s', item.id);
}
}
}
function *syncMissingUsers() {
var missingUsers = [];
var names = Object.keys(npmUsernames);
@@ -536,8 +809,36 @@ SyncModuleWorker.prototype._sync = function *(name, pkg) {
}
}
yield [syncDes(), syncTag(), syncReadme(), syncMissingStarUsers(), syncMissingUsers()];
return versionNames;
// sync diff npm package maintainers
function* syncNpmPackageMaintainers() {
if (diffNpmMaintainers.length === 0) {
return;
}
that.log(' [%s] syncing %d diff package maintainers: %j',
name, diffNpmMaintainers.length, diffNpmMaintainers);
var res = yield gather(diffNpmMaintainers.map(function (item) {
return _saveMaintainer(name, item[0], item[1]);
}));
for (var i = 0; i < res.length; i++) {
var r = res[i];
if (r.error) {
that.log(' save package maintainer error, %s', r.error.message);
}
}
}
yield [
syncDes(),
syncTag(),
syncReadme(),
syncDeprecateds(),
syncMissingStarUsers(),
syncMissingUsers(),
syncNpmPackageMaintainers(),
];
return syncedVersionNames;
};
SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePackage) {
@@ -558,13 +859,17 @@ SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePack
};
var dependencies = Object.keys(sourcePackage.dependencies || {});
var devDependencies = Object.keys(sourcePackage.devDependencies || {});
var devDependencies = [];
if (this.syncDevDependencies) {
devDependencies = Object.keys(sourcePackage.devDependencies || {});
}
that.log(' [%s:%d] syncing, version: %s, dist: %j, no deps: %s, ' +
'publish on cnpm: %s, dependencies: %d, devDependencies: %d',
'publish on cnpm: %s, dependencies: %d, devDependencies: %d, syncDevDependencies: %s',
sourcePackage.name, versionIndex, sourcePackage.version,
sourcePackage.dist, that.noDep, that._publish,
dependencies.length, devDependencies.length);
dependencies.length,
devDependencies.length, this.syncDevDependencies);
if (dependencies.length > config.maxDependencies) {
dependencies = dependencies.slice(0, config.maxDependencies);
@@ -598,7 +903,7 @@ SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePack
try {
// get tarball
var r = yield *urllib.request(downurl, options);
var r = yield urllib.request(downurl, options);
var statusCode = r.status || -1;
// https://github.com/cnpm/cnpmjs.org/issues/325
// if (statusCode === 404) {
@@ -705,15 +1010,8 @@ SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePack
}
};
SyncModuleWorker.sync = function *(name, username, options) {
SyncModuleWorker.sync = function* (name, username, options) {
options = options || {};
var pkg = yield npm.get(name);
if (!pkg || !pkg._rev) {
return {
ok: false,
pkg: pkg
};
}
var result = yield Log.create({name: name, username: username});
var worker = new SyncModuleWorker({
logId: result.id,
@@ -723,9 +1021,5 @@ SyncModuleWorker.sync = function *(name, username, options) {
publish: options.publish,
});
worker.start();
return {
ok: true,
logId: result.id,
pkg: pkg
};
return result.id;
};

View File

@@ -9,6 +9,7 @@
*/
'use strict';
/* jshint -W032 */
/**
* Module dependencies.
@@ -149,22 +150,21 @@ var UPDATE_SYNC_NUM_SQL = multiline(function () {;/*
UPDATE
total
SET
sync_status = ?,
need_sync_num = ?,
success_sync_num = ?,
fail_sync_num = ?,
left_sync_num = ?,
last_sync_module = ?
?
WHERE
name="total";
*/});
exports.updateSyncNum = function (params, callback) {
var query = [
params.syncStatus, params.need || 0,
params.success || 0, params.fail || 0, params.left || 0,
params.lastSyncModule,
];
mysql.query(UPDATE_SYNC_NUM_SQL, query, callback);
var arg = {
sync_status: params.syncStatus,
need_sync_num: params.need || 0,
success_sync_num: params.success || 0,
fail_sync_num: params.fail || 0,
left_sync_num: params.left || 0,
last_sync_module: params.lastSyncModule
};
mysql.query(UPDATE_SYNC_NUM_SQL, [arg], callback);
};
thunkify(exports);

View File

@@ -9,6 +9,7 @@
*/
'use strict';
/* jshint -W032 */
/**
* Module dependencies.
@@ -20,59 +21,11 @@ var config = require('../config');
var mysql = require('../common/mysql');
var multiline = require('multiline');
var SELECT_USER_SQL = multiline(function () {;/*
SELECT
id, rev, name, email, salt, password_sha, ip,
roles, json, npm_user, gmt_create, gmt_modified
FROM
user
WHERE
name=?;
*/});
exports.get = function (name, callback) {
mysql.queryOne(SELECT_USER_SQL, [name], function (err, row) {
if (row) {
try {
row.roles = row.roles ? JSON.parse(row.roles) : [];
} catch (e) {
row.roles = [];
}
try {
row.json = row.json ? JSON.parse(row.json) : null;
} catch (e) {
row.json = null;
}
}
callback(err, row);
});
};
function passwordSha(password, salt) {
return utility.sha1(password + salt);
}
exports.auth = function (name, password, callback) {
exports.get(name, function (err, row) {
if (err || !row) {
return callback(err, row);
}
var sha = passwordSha(password, row.salt);
if (row.password_sha !== sha) {
row = null;
}
callback(null, row);
});
};
var INSERT_USER_SQL = multiline(function () {;/*
INSERT INTO
user(rev, name, email, salt, password_sha,
ip, roles, gmt_create, gmt_modified)
VALUES
(?, ?, ?, ?, ?, ?, ?, now(), now());
*/});
var INSERT_USER_SQL = 'INSERT INTO user SET ?';
exports.add = function (user, callback) {
var roles = user.roles || [];
try {
@@ -81,8 +34,22 @@ exports.add = function (user, callback) {
roles = '[]';
}
var rev = '1-' + utility.md5(JSON.stringify(user));
var values = [rev, user.name, user.email, user.salt, user.password_sha, user.ip, roles];
mysql.query(INSERT_USER_SQL, values, function (err) {
var now = new Date();
var arg = {
rev: rev,
name: user.name,
email: user.email,
salt: user.salt,
password_sha: user.password_sha,
ip: user.ip,
roles: roles,
gmt_create: now,
gmt_modified: now
};
mysql.query(INSERT_USER_SQL, [arg], function (err) {
callback(err, {rev: rev});
});
};
@@ -91,13 +58,7 @@ var UPDATE_USER_SQL = multiline(function () {;/*
UPDATE
user
SET
rev=?,
email=?,
salt=?,
password_sha=?,
ip=?,
roles=?,
gmt_modified=now()
?
WHERE
name=? AND rev=?;
*/});
@@ -119,8 +80,17 @@ exports.update = function (user, callback) {
roles = '[]';
}
var values = [newRev, user.email, user.salt, user.password_sha, user.ip, roles, user.name, rev];
mysql.query(UPDATE_USER_SQL, values, function (err, data) {
var arg = {
rev: newRev,
email: user.email,
salt: user.salt,
password_sha: user.password_sha,
ip: user.ip,
roles: roles,
gmt_modified: new Date()
};
mysql.query(UPDATE_USER_SQL, [arg, user.name, rev], function (err, data) {
if (err) {
return callback(err);
}
@@ -132,7 +102,42 @@ thunkify(exports);
exports.passwordSha = passwordSha;
exports.saveNpmUser = function *(user) {
var SELECT_USER_SQL = 'SELECT \
id, rev, name, email, salt, password_sha, ip, \
roles, json, npm_user, gmt_create, gmt_modified \
FROM \
user \
WHERE \
name=?;';
exports.get = function* (name) {
var row = yield mysql.queryOne(SELECT_USER_SQL, [name]);
if (row) {
try {
row.roles = row.roles ? JSON.parse(row.roles) : [];
} catch (e) {
row.roles = [];
}
try {
row.json = row.json ? JSON.parse(row.json) : null;
} catch (e) {
row.json = null;
}
}
return row;
};
exports.auth = function* (name, password) {
var row = yield* exports.get(name);
if (row) {
var sha = passwordSha(password, row.salt);
if (row.password_sha !== sha) {
row = null;
}
}
return row;
};
exports.saveNpmUser = function* (user) {
var sql = 'SELECT id, json FROM user WHERE name=?;';
var row = yield mysql.queryOne(sql, [user.name]);
if (!row) {
@@ -145,17 +150,47 @@ exports.saveNpmUser = function *(user) {
}
};
var LIST_BY_NAMES_SQL = multiline(function () {;/*
SELECT
id, name, email, json
FROM
user
WHERE
name in (?);
*/});
exports.listByNames = function *(names) {
exports.saveCustomUser = function* (data) {
var sql = 'SELECT id, json FROM user WHERE name=?;';
var row = yield mysql.queryOne(sql, [data.user.login]);
var salt = data.salt || '0';
var password_sha = data.password_sha || '0';
var ip = data.ip || '0';
var rev = rev || '1-' + data.user.login;
var json = JSON.stringify(data.user);
if (!row) {
sql = 'INSERT INTO user(npm_user, json, rev, name, email, salt, password_sha, ip, gmt_create, gmt_modified) \
VALUES(2, ?, ?, ?, ?, ?, ?, ?, now(), now());';
yield mysql.query(sql, [
json, rev, data.user.login, data.user.email,
salt, password_sha, ip
]);
} else {
sql = 'UPDATE user SET json=?, rev=?, salt=?, password_sha=?, ip=? WHERE id=?;';
yield mysql.query(sql, [
json, rev,
salt, password_sha, ip,
row.id
]);
}
};
var LIST_BY_NAMES_SQL = 'SELECT \
id, name, email, json \
FROM \
user \
WHERE \
name in (?);';
exports.listByNames = function* (names) {
if (names.length === 0) {
return [];
}
return yield mysql.query(LIST_BY_NAMES_SQL, [names]);
};
var SEARCH_SQL = 'SELECT id, name, email, json FROM user WHERE name LIKE ? LIMIT ?;';
exports.search = function* (query, options) {
var limit = options.limit;
query = query + '%';
return yield mysql.query(SEARCH_SQL, [query, limit]);
};

View File

@@ -15,7 +15,6 @@
* Module dependencies.
*/
var middlewares = require('koa-middlewares');
var limit = require('../middleware/limit');
var login = require('../middleware/login');
var publishable = require('../middleware/publishable');
@@ -24,9 +23,18 @@ var total = require('../controllers/total');
var mod = require('../controllers/registry/module');
var user = require('../controllers/registry/user');
var sync = require('../controllers/sync');
var userPackage = require('../controllers/registry/user_package');
function routes(app) {
app.get('/', middlewares.jsonp(), total.show);
function* jsonp(next) {
yield* next;
if (this.body) {
this.jsonp = this.body;
}
}
app.get('/', jsonp, total.show);
//before /:name/:version
//get all modules, for npm search
@@ -36,30 +44,38 @@ function routes(app) {
app.get('/-/short', mod.listAllModuleNames);
// module
// scope package: params: [$name]
app.get(/^\/(@[\w\-\.]+\/[\w\-\.]+)$/, syncByInstall, mod.show);
// scope package: params: [$name, $version]
app.get(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/([\w\.\-]+)$/, syncByInstall, mod.get);
app.get('/:name', syncByInstall, mod.show);
app.get('/:name/:version', syncByInstall, mod.get);
// try to add module
app.put('/:name', login, publishable, mod.add);
app.put(/^\/(@[\w\-\.]+\/[\w\-\.]+)$/, login, publishable, mod.addPackageAndDist);
app.put('/:name', login, publishable, mod.addPackageAndDist);
// sync from source npm
app.put('/:name/sync', sync.sync);
app.get('/:name/sync/log/:id', sync.getSyncLog);
app.put(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/([\w\-\.]+)$/, login, mod.updateTag);
app.put('/:name/:tag', login, mod.updateTag);
// need limit by ip
app.get(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/download\/(@[\w\-\.]+\/[\w\-\.]+)$/, limit, mod.download);
app.get('/:name/download/:filename', limit, mod.download);
app.get(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/\-\/(@[\w\-\.]+\/[\w\-\.]+)$/, limit, mod.download);
app.get('/:name/-/:filename', limit, mod.download);
// put tarball
// https://registry.npmjs.org/cnpmjs.org/-/cnpmjs.org-0.0.0.tgz/-rev/1-c85bc65e8d2470cc4d82b8f40da65b8e
app.put('/:name/-/:filename/-rev/:rev', login, publishable, mod.upload);
// delete tarball
app.delete(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/download\/(@[\w\-\.]+\/[\w\-\.]+)\/\-rev\/([\w\-\.]+)$/,
login, publishable, mod.removeTar);
app.delete('/:name/download/:filename/-rev/:rev', login, publishable, mod.removeTar);
// put package.json to module
app.put('/:name/:version/-tag/latest', login, publishable, mod.updateLatest);
// update module, unpublish will PUT this
app.put(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/\-rev\/([\w\-\.]+)$/, login, publishable, mod.updateOrRemove);
app.delete(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/\-rev\/([\w\-\.]+)$/, login, publishable, mod.removeAll);
app.put('/:name/-rev/:rev', login, publishable, mod.updateOrRemove);
app.delete('/:name/-rev/:rev', login, publishable, mod.removeAll);
@@ -68,8 +84,9 @@ function routes(app) {
app.put('/-/user/org.couchdb.user::name', user.add);
app.get('/-/user/org.couchdb.user::name', user.show);
app.put('/-/user/org.couchdb.user::name/-rev/:rev', login, user.update);
// _session
app.post('/_session', user.authSession);
// list all packages of user
app.get('/-/by-user/:user', userPackage.list);
}
module.exports = routes;

View File

@@ -20,23 +20,40 @@ var user = require('../controllers/web/user');
var sync = require('../controllers/sync');
var total = require('../controllers/total');
var dist = require('../controllers/web/dist');
var badge = require('../controllers/web/badge');
function routes(app) {
app.get('/total', total.show);
// scope package without version
app.get(/\/package\/(@[\w\-\.]+\/[\w\-\.]+)$/, pkg.display);
// scope package with version
app.get(/\/package\/(@[\w\-\.]+\/[\w\-\.]+)\/([\w\d\.]+)$/, pkg.display);
app.get('/package/:name', pkg.display);
app.get('/package/:name/:version', pkg.display);
app.get('/privates', pkg.listPrivates);
app.get(/\/browse\/keyword\/(@[\w\-\.]+\/[\w\-\.]+)$/, pkg.search);
app.get('/browse/keyword/:word', pkg.search);
app.get('/~:name', user.display);
app.get('/sync/:name', pkg.displaySync);
app.put('/sync/:name', sync.sync);
// params: [$name, $id]
app.get(/\/sync\/(@[\w\-\.]+\/[\w\-\.]+)\/log\/(\d+)$/, sync.getSyncLog);
app.get('/sync/:name/log/:id', sync.getSyncLog);
app.get('/sync', pkg.displaySync);
app.get('/_list/search/search', pkg.rangeSearch);
app.get(/^\/dist(\/.*)?/, dist.list);
app.get(/^\/badge\/v\/([@\w\-\.\/]+)\.svg$/, badge.version);
}
module.exports = routes;

View File

@@ -22,18 +22,17 @@ var middlewares = require('koa-middlewares');
var routes = require('../routes/registry');
var logger = require('../common/logger');
var config = require('../config');
var session = require('../common/session');
var auth = require('../middleware/auth');
var staticCache = require('../middleware/static');
var notFound = require('../middleware/registry_not_found');
middlewares.jsonp(app);
app.use(middlewares.rt({headerName: 'X-ReadTime'}));
app.use(middlewares.rewrite('/favicon.ico', '/favicon.png'));
app.use(staticCache);
app.keys = ['todokey', config.sessionSecret];
app.proxy = true;
app.use(session);
app.use(middlewares.bodyParser({jsonLimit: config.jsonLimit}));
app.use(auth());
app.use(notFound);
@@ -56,6 +55,7 @@ routes(app);
*/
app.on('error', function (err, ctx) {
// console.log(err.stack)
err.url = err.url || ctx.request.url;
logger.error(err);
});

View File

@@ -21,7 +21,6 @@ var fs = require('fs');
var koa = require('koa');
var middlewares = require('koa-middlewares');
var markdown = require('koa-markdown');
var session = require('../common/session');
var opensearch = require('../middleware/opensearch');
var notFound = require('../middleware/web_not_found');
var staticCache = require('../middleware/static');
@@ -41,7 +40,6 @@ app.use(staticCache);
app.use(opensearch);
app.keys = ['todokey', config.sessionSecret];
app.proxy = true;
app.use(session);
app.use(middlewares.bodyParser());
app.use(auth());
app.use(notFound);
@@ -107,6 +105,7 @@ routes(app);
app.on('error', function (err, ctx) {
err.url = err.url || ctx.request.url;
console.log(err.stack);
logger.error(err);
});

View File

@@ -0,0 +1,139 @@
/**!
* cnpmjs.org - services/default_user_service.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var gravatar = require('gravatar');
var User = require('../proxy/user');
var isAdmin = require('../lib/common').isAdmin;
var config = require('../config');
// User: https://github.com/cnpm/cnpmjs.org/wiki/Use-Your-Own-User-Authorization#user-data-structure
// {
// "login": "fengmk2",
// "email": "fengmk2@gmail.com",
// "name": "Yuan Feng",
// "html_url": "http://fengmk2.github.com",
// "avatar_url": "https://avatars3.githubusercontent.com/u/156269?s=460",
// "im_url": "",
// "site_admin": false,
// "scopes": ["@org1", "@org2"]
// }
module.exports = DefaultUserService;
function convertToUser(row) {
var user = {
login: row.name,
email: row.email,
name: row.name,
html_url: 'http://cnpmjs.org/~' + row.name,
avatar_url: '',
im_url: '',
site_admin: isAdmin(row.name),
scopes: config.scopes
};
if (row.json) {
var data = row.json;
if (data.login) {
// custom user
user = data;
} else {
// npm user
if (data.avatar) {
user.avatar_url = data.avatar;
}
if (data.fullname) {
user.name = data.fullname;
}
if (data.homepage) {
user.html_url = data.homepage;
}
if (data.twitter) {
user.im_url = 'https://twitter.com/' + data.twitter;
}
}
}
if (!user.avatar_url) {
user.avatar_url = gravatar.url(user.email, {s: '50', d: 'retro'}, true);
}
return user;
}
function DefaultUserService() {}
var proto = DefaultUserService.prototype;
/**
* Auth user with login name and password
* @param {String} login login name
* @param {String} password login password
* @return {User}
*/
proto.auth = function* (login, password) {
var row = yield* User.auth(login, password);
if (!row) {
return null;
}
return convertToUser(row);
};
/**
* Get user by login name
* @param {String} login login name
* @return {User}
*/
proto.get = function* (login) {
var row = yield* User.get(login);
if (!row) {
return null;
}
return convertToUser(row);
};
/**
* List users
* @param {Array<String>} logins login names
* @return {Array<User>}
*/
proto.list = function* (logins) {
var rows = yield* User.listByNames(logins);
var users = [];
rows.forEach(function (row) {
users.push(convertToUser(row));
});
return users;
};
/**
* Search users
* @param {String} query query keyword
* @param {Object} [options] optional query params
* - {Number} limit match users count, default is `20`
* @return {Array<User>}
*/
proto.search = function* (query, options) {
options = options || {};
options.limit = parseInt(options.limit);
if (!options.limit || options.limit < 0) {
options.limit = 20;
}
var rows = yield* User.search(query, options);
var users = [];
rows.forEach(function (row) {
users.push(convertToUser(row));
});
return users;
};

94
services/package.js Normal file
View File

@@ -0,0 +1,94 @@
/**!
* cnpmjs.org - services/package.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var Module = require('../proxy/module');
var ModuleMaintainer = require('../proxy/module_maintainer');
var User = require('../proxy/user');
exports.listMaintainers = function* (name) {
var names = yield* ModuleMaintainer.get(name);
if (names.length === 0) {
return names;
}
var users = yield* User.listByNames(names);
return users.map(function (user) {
return {
name: user.name,
email: user.email
};
});
};
exports.listMaintainerNamesOnly = function* (name) {
return yield* ModuleMaintainer.get(name);
};
exports.addMaintainers = function* (name, usernames) {
return yield* ModuleMaintainer.addMulti(name, usernames);
};
exports.updateMaintainers = function* (name, usernames) {
var rs = yield [
ModuleMaintainer.update(name, usernames),
Module.updateLastModified(name),
];
return rs[0];
};
exports.removeAllMaintainers = function* (name) {
return yield* ModuleMaintainer.removeAll(name);
};
exports.authMaintainer = function* (packageName, username) {
var rs = yield [
ModuleMaintainer.get(packageName),
Module.getLatest(packageName)
];
var maintainers = rs[0];
var latestMod = rs[1];
if (maintainers.length === 0) {
// if not found maintainers, try to get from latest module package info
var ms = latestMod && latestMod.package && latestMod.package.maintainers;
if (ms && ms.length > 0) {
maintainers = ms.map(function (user) {
return user.name;
});
}
}
var isMaintainer = false;
if (latestMod && !latestMod.package._publish_on_cnpm) {
// no one can update public package maintainers
// public package only sync from source npm registry
isMaintainer = false;
} else if (maintainers.length === 0) {
// no maintainers, meaning this module is free for everyone
isMaintainer = true;
} else if (maintainers.indexOf(username) >= 0) {
isMaintainer = true;
}
return {
isMaintainer: isMaintainer,
maintainers: maintainers
};
};
exports.isMaintainer = function* (name, username) {
var result = yield* exports.authMaintainer(name, username);
return result.isMaintainer;
};

56
services/user.js Normal file
View File

@@ -0,0 +1,56 @@
/**!
* cnpmjs.org - services/user.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var config = require('../config');
if (!config.userService) {
var DefaultUserService = require('./default_user_service');
config.userService = new DefaultUserService();
config.customUserService = false;
} else {
config.customUserService = true;
}
config.scopes = config.scopes || [];
function convertUser(user) {
if (!user) {
return null;
}
user.scopes = user.scopes || [];
if (user.scopes.length === 0 && config.scopes.length > 0) {
user.scopes = config.scopes.slice();
}
return user;
}
exports.auth = function* (login, password) {
var user = yield* config.userService.auth(login, password);
return convertUser(user);
};
exports.get = function* (login) {
var user = yield* config.userService.get(login);
return convertUser(user);
};
exports.list = function* (logins) {
var users = yield* config.userService.list(logins);
return users.map(convertUser);
};
exports.search = function* (query, options) {
var users = yield* config.userService.search(query, options);
return users.map(convertUser);
};

View File

@@ -16,14 +16,14 @@
var debug = require('debug')('cnpmjs.org:sync:index');
var co = require('co');
var ms = require('ms');
var ms = require('humanize-ms');
var util = require('util');
var utility = require('utility');
var config = require('../config');
var mail = require('../common/mail');
var Total = require('../proxy/total');
var logger = require('../common/logger');
var startSyncDist = require('./sync_dist');
var syncDistWorker = require('./sync_dist');
var sync = null;
@@ -58,14 +58,14 @@ var handleSync = co(function *() {
var data;
var error;
try {
var data = yield *sync();
data = yield* sync();
} catch (err) {
error = err;
error.message += ' (sync package error)';
logger.syncError(error);
}
if (config.debug) {
error && console.error(error.stack);
data && console.log(data);
} else {
data && logger.syncInfo(data);
if (!config.debug) {
sendMailToAdmin(error, data, new Date());
}
syncing = false;
@@ -74,22 +74,33 @@ var handleSync = co(function *() {
if (sync) {
handleSync();
setInterval(handleSync, ms(config.syncInterval));
var syncInterval = ms(config.syncInterval);
var minSyncInterval = ms('5m');
if (!syncInterval || syncInterval < minSyncInterval) {
syncInterval = minSyncInterval;
}
setInterval(handleSync, syncInterval);
}
/**
* sync dist(node.js and phantomjs)
*/
var syncingDist = false;
var syncDist = co(function* syncDist() {
if (syncingDist) {
return;
}
syncingDist = true;
logger.info('Start syncing dist...');
logger.syncInfo('Start syncing dist...');
try {
yield * startSyncDist();
yield* syncDistWorker();
yield* syncDistWorker.syncPhantomjsDir();
} catch (err) {
err.message += ' (sync dist error)';
logger.warn('Sync dist error: %s: %s\n%s', err.name, err.message, err.stack);
sendMailToAdmin(err, null, new Date());
logger.syncError(err);
if (config.noticeSyncDistError) {
sendMailToAdmin(err, null, new Date());
}
}
syncingDist = false;
});
@@ -98,7 +109,45 @@ if (config.syncDist) {
syncDist();
setInterval(syncDist, ms(config.syncInterval));
} else {
logger.info('sync dist disable');
logger.syncInfo('sync dist disable');
}
/**
* sync popular modules
*/
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 *require('./sync_popular');
} 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) {
syncPopular();
setInterval(syncPopular, ms(config.syncPopularInterval));
} else {
logger.syncInfo('sync popular module disable');
}
function sendMailToAdmin(err, result, syncTime) {
@@ -132,10 +181,10 @@ function sendMailToAdmin(err, result, syncTime) {
syncTime, result.successes.length, result.successes.slice(0, 10));
}
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);
if (type && type !== 'log') {
mail[type](to, subject, html, function (err) {
if (err) {
logger.info('send email with type: %s, subject: %s, html: %s', type, subject, html);
logger.error(err);
}
});

View File

@@ -15,8 +15,7 @@
*/
var debug = require('debug')('cnpmjs.org:sync:sync_all');
var eventproxy = require('eventproxy');
var ms = require('ms');
var ms = require('humanize-ms');
var utility = require('utility');
var config = require('../config');
var Status = require('./status');

View File

@@ -16,12 +16,14 @@
var debug = require('debug')('cnpmjs.org:sync:sync_dist');
var fs = require('fs');
var urllib = require('co-urllib');
var urllib = require('../common/urllib');
var co = require('co');
var bytes = require('bytes');
var crypto = require('crypto');
var utility = require('utility');
var thunkify = require('thunkify-wrap');
var cheerio = require('cheerio');
var urlResolve = require('url').resolve;
var common = require('../lib/common');
var Dist = require('../proxy/dist');
var config = require('../config');
@@ -31,14 +33,13 @@ var logger = require('../common/logger');
var disturl = config.disturl;
var USER_AGENT = 'distsync.cnpmjs.org/' + config.version + ' ' + urllib.USER_AGENT;
// <a href="latest/">latest/</a> 02-May-2014 14:45 -
// <a href="node-v0.4.10.tar.gz">node-v0.4.10.tar.gz</a> 26-Aug-2011 16:22 12410018
var FILE_RE = /^<a[^>]+>([^<]+)<\/a>\s+(\d+\-\w+\-\d+ \d+\:\d+)\s+([\-\d]+)/;
module.exports = sync;
function* sync(name) {
name = name || '/';
if (name[name.length - 1] !== '/') {
name += '/';
}
yield* syncDir(name);
}
@@ -56,7 +57,7 @@ function* syncDir(fullname, info) {
}
}
logger.info('sync remote:%s got %d new items, %d dirs, %d files to sync',
logger.syncInfo('sync remote:%s got %d new items, %d dirs, %d files to sync',
fullname, news.length, dirs.length, files.length);
for (var i = 0; i < files.length; i++) {
@@ -69,18 +70,23 @@ function* syncDir(fullname, info) {
}
if (info) {
logger.info('Save dir:%s %j to database', fullname, info);
logger.syncInfo('Save dir:%s %j to database', fullname, info);
yield* Dist.savedir(info);
}
logger.info('Sync %s finished, %d dirs, %d files',
logger.syncInfo('Sync %s finished, %d dirs, %d files',
fullname, dirs.length, files.length);
}
function* syncFile(info) {
var name = info.parent + info.name;
name = process.pid + name.replace(/\//g, '_'); // make sure no parent dir
var isPhantomjsURL = false;
var downurl = disturl + info.parent + info.name;
if (info.downloadURL) {
downurl = info.downloadURL;
isPhantomjsURL = true;
}
var filepath = common.getTarballFilepath(name);
var ws = fs.createWriteStream(filepath);
@@ -94,11 +100,13 @@ function* syncFile(info) {
};
try {
logger.info('downloading %s %s to %s', bytes(info.size), downurl, filepath);
logger.syncInfo('downloading %s %s to %s, isPhantomjsURL: %s',
bytes(info.size), downurl, filepath, isPhantomjsURL);
// get tarball
var r = yield *urllib.request(downurl, options);
var r = yield urllib.request(downurl, options);
var statusCode = r.status || -1;
logger.info('download %s got status %s, headers: %j', downurl, statusCode, r.headers);
logger.syncInfo('download %s got status %s, headers: %j',
downurl, statusCode, r.headers);
if (statusCode !== 200) {
var err = new Error('Download ' + downurl + ' fail, status: ' + statusCode);
err.name = 'DownloadDistFileError';
@@ -121,7 +129,17 @@ function* syncFile(info) {
throw err;
}
if (dataSize !== info.size) {
if (isPhantomjsURL) {
debug('real size: %s, expect size: %s', dataSize, info.size);
if (dataSize < info.size) {
// phantomjs download page only show `6.7 MB`
var err = new Error('Download ' + downurl + ' file size is '
+ dataSize + ' not match ' + info.size);
err.name = 'DownloadDistFileSizeError';
throw err;
}
info.size = dataSize;
} else if (info.size > 0 && dataSize !== info.size) {
var err = new Error('Download ' + downurl + ' file size is '
+ dataSize + ' not match ' + info.size);
err.name = 'DownloadDistFileSizeError';
@@ -136,58 +154,168 @@ function* syncFile(info) {
};
// upload to NFS
logger.info('uploading %s to nfs:%s', filepath, args.key);
logger.syncInfo('uploading %s to nfs:%s', filepath, args.key);
var result = yield nfs.upload(filepath, args);
info.url = result.url || result.key;
info.sha1 = shasum;
logger.info('upload %s to nfs:%s with size:%d, sha1:%s',
logger.syncInfo('upload %s to nfs:%s with size:%d, sha1:%s',
args.key, info.url, info.size, info.sha1);
} finally {
// remove tmp file whatever
fs.unlink(filepath, utility.noop);
}
logger.info('Sync dist file: %j done', info);
logger.syncInfo('Sync dist file: %j done', info);
yield* Dist.savefile(info);
}
// <a href="latest/">latest/</a> 02-May-2014 14:45 -
// <a href="node-v0.4.10.tar.gz">node-v0.4.10.tar.gz</a> 26-Aug-2011 16:22 12410018
var FILE_RE = /^<a[^>]+>([^<]+)<\/a>\s+(\d+\-\w+\-\d+ \d+\:\d+)\s+([\-\d]+)/;
// */docs/api/
var DOC_API_RE = /\/docs\/api\/$/;
// <li><a href="documentation.html">About these Docs</a></li>
// <li><a href="synopsis.html">Synopsis</a></li>
// <li><a href="assert.html">Assertion Testing</a></li>
// <li><a href="buffer.html">Buffer</a></li>
// <li><a href="addons.html">C/C++ Addons</a></li>
// <li><a href="child_process.html">Child Processes</a></li>
// <div id="gtoc">
// <p>
// <a href="index.html" name="toc">Index</a> |
// <a href="all.html">View on single page</a> |
// <a href="index.json">View as JSON</a>
// </p>
// </div>
var DOC_API_FILE_ALL_RE = /<a[^"]+\"(\w+\.(?:html|json))\"[^>]*>[^<]+<\/a>/gm;
var DOC_API_FILE_RE = /<a[^"]+\"(\w+\.(?:html|json))\"[^>]*>[^<]+<\/a>/;
function* listdir(fullname) {
var url = disturl + fullname;
var result = yield* urllib.request(url, {
var isDocPath = false;
if (DOC_API_RE.test(fullname)) {
isDocPath = true;
url += 'index.html';
}
var result = yield urllib.request(url, {
timeout: 60000,
});
debug('listdir %s got %s, %j', url, result.status, result.headers);
var html = result.data && result.data.toString() || '';
var lines = html.split('\n');
var items = [];
for (var i = 0; i < lines.length; i++) {
var m = FILE_RE.exec(lines[i].trim());
if (!m) {
continue;
}
var itemName = m[1].replace(/^\/+/, '');
if (!itemName) {
continue;
}
// "last-modified":"Tue, 11 Mar 2014 22:44:36 GMT"
var date = result.headers['last-modified'] || result.headers.date || '';
// filter /nightlies/*
if (itemName.indexOf('nightlies/') === 0) {
continue;
if (isDocPath) {
// add assets/
items.push({
name: 'assets/',
date: date,
size: '-',
type: 'dir',
parent: fullname,
});
var needJSON = false;
var htmlfileNames = [];
var lines = html.match(DOC_API_FILE_ALL_RE) || [];
for (var i = 0; i < lines.length; i++) {
var m = DOC_API_FILE_RE.exec(lines[i].trim());
if (!m) {
continue;
}
var itemName = m[1];
items.push({
name: itemName,
date: date,
size: 0,
type: 'file',
parent: fullname,
});
if (itemName.indexOf('.json') > 0) {
needJSON = true;
}
if (itemName.indexOf('.html') > 0 && itemName !== 'index.html') {
htmlfileNames.push(itemName);
}
}
debug('listdir %s got %j', fullname, htmlfileNames);
if (needJSON) {
// node >= 0.8.0
htmlfileNames.forEach(function (itemName) {
items.push({
name: itemName.replace('.html', '.json'), // download *.json format
date: date,
size: 0,
type: 'file',
parent: fullname,
});
});
}
} else {
var lines = html.split('\n');
for (var i = 0; i < lines.length; i++) {
var m = FILE_RE.exec(lines[i].trim());
if (!m) {
continue;
}
var itemName = m[1].replace(/^\/+/, '');
if (!itemName) {
continue;
}
// filter /nightlies/*
if (itemName.indexOf('nightlies/') === 0) {
continue;
}
items.push({
name: itemName, // 'SHASUMS.txt', 'x64/'
date: m[2],
size: m[3] === '-' ? '-' : parseInt(m[3]),
type: m[3] === '-' ? 'dir' : 'file',
parent: fullname, // '/', '/v0.10.28/'
});
}
}
// node <= v0.11.11, /docs/ is not list, has a index.html
if (items.length === 0 && /\/docs\/$/.test(fullname)) {
items.push({
name: 'api/',
date: date,
size: '-',
type: 'dir',
parent: fullname,
});
// sh_main.js
// sh_javascript.min.js
items.push({
name: 'sh_main.js',
date: date,
size: 0,
type: 'file',
parent: fullname,
});
items.push({
name: itemName, // 'SHASUMS.txt', 'x64/'
date: m[2],
size: m[3] === '-' ? '-' : parseInt(m[3]),
type: m[3] === '-' ? 'dir' : 'file',
parent: fullname, // '/', '/v0.10.28/'
name: 'sh_javascript.min.js',
date: date,
size: 0,
type: 'file',
parent: fullname,
});
}
return items;
}
sync.listdir = listdir;
sync.listdiff = function* listdiff(fullname) {
sync.listdiff = function* (fullname) {
var items = yield* listdir(fullname);
if (items.length === 0) {
return items;
@@ -217,3 +345,105 @@ sync.listdiff = function* listdiff(fullname) {
}
return news;
};
function* syncPhantomjsDir() {
var fullname = '/phantomjs/';
var files = yield* sync.listPhantomjsDiff(fullname);
logger.syncInfo('sync remote:%s got %d files to sync',
fullname, files.length);
for (var i = 0; i < files.length; i++) {
yield* syncFile(files[i]);
}
logger.syncInfo('SyncPhantomjsDir %s finished, %d files',
fullname, files.length);
}
sync.syncPhantomjsDir = syncPhantomjsDir;
// <tr class="iterable-item" id="download-301626">
// <td class="name"><a class="execute" href="/ariya/phantomjs/downloads/phantomjs-1.9.7-windows.zip">phantomjs-1.9.7-windows.zip</a></td>
// <td class="size">6.7 MB</td>
// <td class="uploaded-by"><a href="/Vitallium">Vitallium</a></td>
// <td class="count">122956</td>
// <td class="date">
// <div>
// <time datetime="2014-01-27T18:29:53.706942" data-title="true">2014-01-27</time>
// </div>
// </td>
// <td class="delete">
//
// </td>
// </tr>
function* listPhantomjsDir(fullname) {
var url = 'https://bitbucket.org/ariya/phantomjs/downloads';
var result = yield urllib.request(url, {
timeout: 60000,
});
debug('listPhantomjsDir %s got %s, %j', url, result.status, result.headers);
var html = result.data && result.data.toString() || '';
var $ = cheerio.load(html);
var items = [];
$('tr.iterable-item').each(function (i, el) {
var $el = $(this);
var $link = $el.find('.name a');
var name = $link.text();
var downloadURL = $link.attr('href');
if (!name || !downloadURL || !/\.(zip|bz2|gz)$/.test(downloadURL)) {
return;
}
downloadURL = urlResolve(url, downloadURL);
var size = parseInt(bytes($el.find('.size').text().toLowerCase().replace(/\s/g, '')));
if (size > 1024 * 1024) {
size -= 1024 * 1024;
} else if (size > 1024) {
size -= 1024;
} else {
size -= 10;
}
var date = $el.find('.date time').text();
items.push({
name: name, // 'SHASUMS.txt', 'x64/'
date: date,
size: size,
type: 'file',
parent: fullname,
downloadURL: downloadURL,
});
});
return items;
}
sync.listPhantomjsDir = listPhantomjsDir;
sync.listPhantomjsDiff = function* (fullname) {
var items = yield* listPhantomjsDir(fullname);
if (items.length === 0) {
return items;
}
var exists = yield* Dist.listdir(fullname);
debug('listdiff %s got %s exists items', fullname, exists.length);
var map = {};
for (var i = 0; i < exists.length; i++) {
var item = exists[i];
map[item.name] = item;
}
var news = [];
for (var i = 0; i < items.length; i++) {
var item = items[i];
var exist = map[item.name];
if (!exist || exist.date !== item.date) {
news.push(item);
continue;
}
// if (item.size !== exist.size) {
// news.push(item);
// continue;
// }
debug('skip %s', item.name);
}
return news;
};

View File

@@ -18,12 +18,11 @@ var config = require('../config');
var Npm = require('../proxy/npm');
var Module = require('../proxy/module');
var Total = require('../proxy/total');
var eventproxy = require('eventproxy');
var SyncModuleWorker = require('../proxy/sync_module_worker');
var debug = require('debug')('cnpmjs.org:sync:sync_hot');
var debug = require('debug')('cnpmjs.org:sync:sync_exist');
var utility = require('utility');
var Status = require('./status');
var ms = require('ms');
var ms = require('humanize-ms');
var thunkify = require('thunkify-wrap');
function intersection(arrOne, arrTwo) {

47
sync/sync_popular.js Normal file
View File

@@ -0,0 +1,47 @@
/*!
* 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';
/**
* Module dependencies.
*/
var SyncModuleWorker = require('../proxy/sync_module_worker');
var debug = require('debug')('cnpmjs.org:sync:sync_popular');
var thunkify = require('thunkify-wrap');
var config = require('../config');
var Npm = require('../proxy/npm');
var utility = require('utility');
var Status = require('./status');
module.exports = function *sync() {
var syncTime = Date.now();
var packages = yield Npm.getPopular(config.topPopular);
var worker = new SyncModuleWorker({
username: 'admin',
name: packages,
concurrency: config.syncConcurrency
});
Status.init({need: packages.length}, worker);
worker.start();
var end = thunkify.event(worker);
yield end();
debug('All popular packages sync done, successes %d, fails %d',
worker.successes.length, worker.fails.length);
return {
successes: worker.successes,
fails: worker.fails
};
};

View File

@@ -0,0 +1,211 @@
/**!
* cnpmjs.org - test/controllers/registry/deprecate.test.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var fs = require('fs');
var path = require('path');
var should = require('should');
var request = require('supertest');
var mm = require('mm');
var pedding = require('pedding');
var app = require('../../../servers/registry');
var utils = require('../../utils');
var fixtures = path.join(path.dirname(path.dirname(__dirname)), 'fixtures');
describe('controllers/registry/deprecate.test.js', function () {
var pkgname = 'testmodule-deprecate';
before(function (done) {
done = pedding(2, done);
var pkg = utils.getPackage(pkgname, '0.0.1', utils.admin);
request(app.listen())
.put('/' + pkgname)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, function (err) {
should.not.exist(err);
pkg = utils.getPackage(pkgname, '0.0.2', utils.admin);
// publish 0.0.2
request(app.listen())
.put('/' + pkgname)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, done);
});
pkg = utils.getPackage(pkgname, '1.0.0', utils.admin);
request(app.listen())
.put('/' + pkgname)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, done);
});
afterEach(mm.restore);
describe('PUT /:name', function () {
it('should deprecate version@1.0.0', function (done) {
request(app.listen())
.put('/' + pkgname)
.set('authorization', utils.adminAuth)
.send({
name: pkgname,
versions: {
'1.0.0': {
deprecated: 'mock test deprecated message 1.0.0'
}
}
})
.expect({
ok: true
})
.expect(201, function (err, res) {
should.not.exist(err);
request(app.listen())
.get('/' + pkgname + '/1.0.0')
.expect(200, function (err, res) {
should.not.exist(err);
res.body.deprecated.should.equal('mock test deprecated message 1.0.0');
// undeprecated
request(app.listen())
.put('/' + pkgname)
.set('authorization', utils.adminAuth)
.send({
name: pkgname,
versions: {
'1.0.0': {
deprecated: ''
}
}
})
.expect({
ok: true
})
.expect(201, function (err) {
should.not.exist(err);
request(app.listen())
.get('/' + pkgname + '/1.0.0')
.expect(200, function (err, res) {
should.not.exist(err);
res.body.deprecated.should.equal('');
done();
});
});
});
});
});
it('should deprecate version@<1.0.0', function (done) {
request(app.listen())
.put('/' + pkgname)
.set('authorization', utils.adminAuth)
.send({
name: pkgname,
versions: {
'1.0.0': {
version: '1.0.0'
},
'0.0.1': {
deprecated: 'mock test deprecated message 0.0.1'
},
'0.0.2': {
deprecated: 'mock test deprecated message 0.0.2'
}
}
})
.expect({
ok: true
})
.expect(201, function (err, res) {
should.not.exist(err);
done = pedding(3, done);
request(app.listen())
.get('/' + pkgname + '/0.0.1')
.expect(200, function (err, res) {
should.not.exist(err);
res.body.deprecated.should.equal('mock test deprecated message 0.0.1');
done();
});
request(app.listen())
.get('/' + pkgname + '/0.0.2')
.expect(200, function (err, res) {
should.not.exist(err);
res.body.deprecated.should.equal('mock test deprecated message 0.0.2');
done();
});
// not change 1.0.0
request(app.listen())
.get('/' + pkgname + '/1.0.0')
.expect(200, function (err, res) {
should.not.exist(err);
res.body.deprecated.should.equal('');
done();
});
});
});
it('should 404 deprecate not exists version', function (done) {
request(app.listen())
.put('/' + pkgname)
.set('authorization', utils.adminAuth)
.send({
name: pkgname,
versions: {
'1.0.1': {
deprecated: 'mock test deprecated message'
},
'1.0.0': {
deprecated: 'mock test deprecated message'
}
}
})
.expect({
error: 'version_error',
reason: 'Some versions: ["1.0.1","1.0.0"] not found'
})
.expect(400, done);
});
it('should 403', function (done) {
request(app.listen())
.put('/' + pkgname)
.set('authorization', utils.otherUserAuth)
.send({
name: pkgname,
versions: {
'1.0.0': {
version: '1.0.0'
},
'0.0.1': {
deprecated: 'mock test deprecated message 0.0.1'
},
'0.0.2': {
deprecated: 'mock test deprecated message 0.0.2'
}
}
})
.expect({
error: 'no_perms',
reason: 'Private mode enable, only admin can publish this module'
})
.expect(403, done);
});
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,105 @@
/*!
* cnpmjs.org - test/controllers/registry/module/public_mode.test.js
* Copyright(c) 2014 dead_horse <dead_horse@qq.com>
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
*/
var should = require('should');
var request = require('supertest');
var mm = require('mm');
var config = require('../../../../config');
var app = require('../../../../servers/registry');
var utils = require('../../../utils');
describe('controllers/registry/module/config_private_packages.test.js', function () {
beforeEach(function () {
mm(config, 'enablePrivate', false);
mm(config, 'forcePublishWithScope', true);
mm(config, 'privatePackages', ['private-package']);
});
after(mm.restore);
it('should publish with tgz base64, addPackageAndDist()', function (done) {
var pkg = utils.getPackage('private-package', '0.0.1', utils.otherUser);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(201, function (err, res) {
should.not.exist(err);
res.body.should.have.keys('ok', 'rev');
res.body.ok.should.equal(true);
pkg = utils.getPackage('private-package', '0.0.1', utils.otherUser);
// upload again should 403
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(403, function (err, res) {
should.not.exist(err);
res.body.should.eql({
error: 'forbidden',
reason: 'cannot modify pre-existing version: 0.0.1'
});
done();
});
});
});
it('should other user publish 403', function (done) {
var pkg = utils.getPackage('private-package', '0.0.2', utils.secondUser);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.secondUserAuth)
.send(pkg)
.expect(/forbidden user/)
.expect(403, done);
});
it('should admin publish 403', function (done) {
var pkg = utils.getPackage('private-package', '0.0.2', utils.admin);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(/forbidden user/)
.expect(403, done);
});
it('should add again new maintainers', function (done) {
request(app)
.put('/private-package/-rev/1')
.send({
maintainers: [{
name: 'cnpmjstest101',
email: 'cnpmjstest101@cnpmjs.org'
}, {
name: 'fengmk2',
email: 'fengmk2@cnpmjs.org'
}]
})
.set('authorization', utils.otherUserAuth)
.expect(201)
.expect('content-type', 'application/json; charset=utf-8', done);
});
it('should remove maintainers', function (done) {
request(app)
.put('/private-package/-rev/1')
.send({
maintainers: [{
name: 'cnpmjstest101',
email: 'cnpmjstest101@cnpmjs.org'
}]
})
.set('authorization', utils.otherUserAuth)
.expect(201)
.expect('content-type', 'application/json; charset=utf-8', done);
});
});

View File

@@ -0,0 +1,100 @@
/**!
* cnpmjs.org - test/controllers/registry/module/maintainer.test.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var should = require('should');
var request = require('supertest');
var mm = require('mm');
var config = require('../../../../config');
var app = require('../../../../servers/registry');
var utils = require('../../../utils');
describe('controllers/registry/module/maintainer.test.js', function () {
var pkgname = '@cnpm/test-package-maintainer';
var pkgURL = '/@' + encodeURIComponent(pkgname.substring(1));
before(function (done) {
app = app.listen(0, function () {
// add scope package
var pkg = utils.getPackage(pkgname, '0.0.1', utils.admin);
request(app)
.put(pkgURL)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, done);
});
});
beforeEach(function () {
mm(config, 'scopes', ['@cnpm', '@cnpmtest']);
});
afterEach(mm.restore);
it('should add new maintainer without custom user service', function (done) {
mm(config, 'customUserService', false);
request(app)
.put('/@cnpm/test-package-maintainer/-rev/1')
.set('authorization', utils.adminAuth)
.send({
maintainers: [
{ name: 'new-maintainer', email: 'new-maintainer@cnpmjs.org' },
{ name: utils.admin, email: utils.admin + '@cnpmjs.org' },
]
})
.expect(201, done);
});
describe('config.customUserService = true', function () {
it('should add new maintainer fail when user not exists', function (done) {
mm(config, 'customUserService', true);
request(app)
.put('/@cnpm/test-package-maintainer/-rev/1')
.set('authorization', utils.adminAuth)
.send({
maintainers: [
{ name: 'new-maintainer-not-exists', email: 'new-maintainer@cnpmjs.org' },
{ name: 'new-maintainer-not-exists2', email: 'new-maintainer@cnpmjs.org' },
{ name: utils.admin, email: utils.admin + '@cnpmjs.org' },
]
})
.expect({
error: 'invalid user name',
reason: 'User: new-maintainer-not-exists, new-maintainer-not-exists2 not exists'
})
.expect(403, done);
});
it('should add new maintainer success when user all exists', function (done) {
mm(config, 'customUserService', true);
request(app)
.put('/@cnpm/test-package-maintainer/-rev/1')
.set('authorization', utils.adminAuth)
.send({
maintainers: [
{ name: 'cnpmjstest101', email: 'cnpmjstest101@cnpmjs.org' },
{ name: 'cnpmjstest102', email: 'cnpmjstest102@cnpmjs.org' },
{ name: utils.admin, email: utils.admin + '@cnpmjs.org' },
]
})
.expect({
ok: true,
id: '@cnpm/test-package-maintainer',
rev: '1'
})
.expect(201, done);
});
});
});

View File

@@ -0,0 +1,804 @@
/*!
* cnpmjs.org - test/controllers/registry/module/public_mode.test.js
* Copyright(c) 2014 dead_horse <dead_horse@qq.com>
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
*/
var should = require('should');
var request = require('supertest');
var path = require('path');
var mm = require('mm');
var pedding = require('pedding');
var config = require('../../../../config');
var app = require('../../../../servers/registry');
var utils = require('../../../utils');
var Module = require('../../../../proxy/module');
var fixtures = path.join(__dirname, '..', '..', '..', 'fixtures');
describe('controllers/registry/module/public_module.test.js', function () {
beforeEach(function () {
mm(config, 'enablePrivate', false);
});
before(function (done) {
mm(config, 'enablePrivate', false);
mm(config, 'forcePublishWithScope', false);
app = app.listen(0, function () {
done = pedding(2, done);
// name: publictestmodule
var pkg = utils.getPackage('publictestmodule', '0.0.1', utils.otherUser);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(201, function (err) {
should.not.exist(err);
pkg = utils.getPackage('publictestmodule', '0.0.2', utils.otherUser);
// publish 0.0.2
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(201, done);
});
// publicputmodule@0.1.9
var testpkg = utils.getPackage('publicputmodule', '0.1.9', utils.otherUser);
request(app)
.put('/' + testpkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(201, done);
});
});
afterEach(mm.restore);
describe('PUT /:name/-rev/id updateMaintainers() in public mode', function () {
beforeEach(function () {
mm(config, 'forcePublishWithScope', false);
});
before(function (done) {
mm(config, 'enablePrivate', false);
mm(config, 'forcePublishWithScope', false);
request(app)
.put('/publictestmodule/-rev/1')
.send({
maintainers: [{
name: 'cnpmjstest101',
email: 'fengmk2@cnpmjs.org'
}]
})
.set('authorization', utils.otherUserAuth)
.expect({"ok":true,"id":"publictestmodule","rev":"1"}, done);
});
it('should add new maintainers', function (done) {
request(app)
.put('/publictestmodule/-rev/1')
.send({
maintainers: [{
name: 'cnpmjstest10',
email: 'fengmk2@cnpmjs.org'
}, {
name: 'cnpmjstest101',
email: 'fengmk2@cnpmjs.org'
}]
})
.set('authorization', utils.otherUserAuth)
.expect(201)
.expect({
ok: true, id: 'publictestmodule', rev: '1'
}, function (err) {
should.not.exist(err);
done = pedding(2, done);
// check maintainers update
request(app)
.get('/publictestmodule')
.expect(200, function (err, res) {
should.not.exist(err);
var pkg = res.body;
pkg.maintainers.should.length(2);
pkg.maintainers.should.eql(pkg.versions['0.0.1'].maintainers);
pkg.maintainers.sort(function (a, b) {
return a.name > b.name ? 1 : -1;
});
pkg.maintainers.should.eql([
{ name: 'cnpmjstest10', email: 'fengmk2@gmail.com' },
{ name: 'cnpmjstest101', email: 'fengmk2@gmail.com' },
]);
done();
});
// /pkg/0.0.1
request(app)
.get('/publictestmodule/0.0.1')
.expect(200, function (err, res) {
should.not.exist(err);
var pkg = res.body;
pkg.maintainers.should.length(2);
pkg.maintainers.sort(function (a, b) {
return a.name > b.name ? 1 : -1;
});
pkg.maintainers.should.eql([
{ name: 'cnpmjstest10', email: 'fengmk2@gmail.com' },
{ name: 'cnpmjstest101', email: 'fengmk2@gmail.com' },
]);
done();
});
});
});
it('should add again new maintainers', function (done) {
request(app)
.put('/publictestmodule/-rev/1')
.send({
maintainers: [{
name: 'cnpmjstest101',
email: 'cnpmjstest101@cnpmjs.org'
}, {
name: 'fengmk2',
email: 'fengmk2@cnpmjs.org'
}]
})
.set('authorization', utils.otherUserAuth)
.expect(201)
.expect('content-type', 'application/json; charset=utf-8', done);
});
it('should add new maintainers by admin', function (done) {
request(app)
.put('/publictestmodule/-rev/1')
.send({
maintainers: [{
name: 'cnpmjstest101',
email: 'cnpmjstest101@cnpmjs.org'
}, {
name: 'fengmk2',
email: 'fengmk2@cnpmjs.org'
}]
})
.set('authorization', utils.adminAuth)
.expect(201)
.expect('content-type', 'application/json; charset=utf-8', done);
});
it('should rm maintainers', function (done) {
request(app)
.put('/publictestmodule/-rev/1')
.send({
maintainers: [{
name: 'cnpmjstest101',
email: 'cnpmjstest101@cnpmjs.org'
}]
})
.set('authorization', utils.otherUserAuth)
.expect(201)
.expect('content-type', 'application/json; charset=utf-8', done);
});
it('should rm again maintainers', function (done) {
request(app)
.put('/publictestmodule/-rev/1')
.send({
maintainers: [{
name: 'cnpmjstest101',
email: 'cnpmjstest101@cnpmjs.org'
}]
})
.set('authorization', utils.otherUserAuth)
.expect(201)
.expect({
id: 'publictestmodule',
rev: '1',
ok: true
}, done);
});
it('should rm all maintainers forbidden 403', function (done) {
request(app)
.put('/publictestmodule/-rev/1')
.send({
maintainers: []
})
.set('authorization', utils.otherUserAuth)
.expect(403)
.expect({error: 'invalid operation', reason: 'Can not remove all maintainers'})
.expect('content-type', 'application/json; charset=utf-8', done);
});
it('should 403 when not maintainer update', function (done) {
request(app)
.put('/publictestmodule/-rev/1')
.send({
maintainers: [{
name: 'cnpmjstest10',
email: 'cnpmjstest10@cnpmjs.org'
}]
})
.set('authorization', utils.secondUserAuth)
.expect(403)
.expect({
error: 'forbidden user',
reason: 'cnpmjstest102 not authorized to modify publictestmodule'
}, done);
});
describe('forcePublishWithScope = true', function () {
beforeEach(function () {
mm(config, 'forcePublishWithScope', true);
});
before(function (done) {
mm(config, 'forcePublishWithScope', true);
mm(config, 'enablePrivate', false);
var pkg = utils.getPackage('@cnpm/publictestmodule', '0.0.1', utils.otherUser);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(201, function (err, res) {
should.not.exist(err);
pkg = utils.getPackage(pkg.name, '0.0.2', utils.otherUser);
// publish 0.0.2
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(201, done);
});
});
it('should 403 add maintainers without scope', function (done) {
request(app)
.put('/publictestmodule/-rev/1')
.send({
maintainers: [{
name: 'cnpmjstest101',
email: 'cnpmjstest101@cnpmjs.org'
}, {
name: 'fengmk2',
email: 'fengmk2@cnpmjs.org'
}]
})
.set('authorization', utils.otherUserAuth)
.expect(403, done);
});
it('should add maintainers ok with scope', function (done) {
request(app)
.put('/@cnpm/publictestmodule/-rev/1')
.send({
maintainers: [{
name: 'cnpmjstest101',
email: 'cnpmjstest101@cnpmjs.org'
}, {
name: 'fengmk2',
email: 'fengmk2@cnpmjs.org'
}]
})
.set('authorization', utils.otherUserAuth)
.expect( { ok: true, id: '@cnpm/publictestmodule', rev: '1' })
.expect(201, done);
});
});
});
describe('PUT /:name publish new flow addPackageAndDist()', function () {
beforeEach(function () {
mm(config, 'enablePrivate', false);
mm(config, 'forcePublishWithScope', false);
});
it('should publish with tgz base64, addPackageAndDist()', function (done) {
var pkg = utils.getPackage('publicpublishmodule', '0.0.2', utils.otherUser);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(201, function (err, res) {
should.not.exist(err);
res.body.should.have.keys('ok', 'rev');
res.body.ok.should.equal(true);
pkg = utils.getPackage('publicpublishmodule', '0.0.2', utils.otherUser);
// upload again should 403
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(403, function (err, res) {
should.not.exist(err);
res.body.should.eql({
error: 'forbidden',
reason: 'cannot modify pre-existing version: 0.0.2'
});
done();
});
});
});
it('should other user pulbish 403', function (done) {
var pkg = utils.getPackage('publicpublishmodule', '0.0.3', utils.secondUser);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.secondUserAuth)
.send(pkg)
.expect(403, done);
});
it('should admin pulbish 403', function (done) {
var pkg = utils.getPackage('publicpublishmodule', '0.0.3', utils.admin);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(403, done);
});
it('should publish with scope, addPackageAndDist()', function (done) {
mm(config, 'forcePublishWithScope', false);
var pkg = utils.getPackage('@cnpm/publicpublishmodule', '0.0.2', utils.otherUser);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(201, function (err, res) {
should.not.exist(err);
res.body.should.have.keys('ok', 'rev');
res.body.ok.should.equal(true);
// upload again should 403
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(403, function (err, res) {
should.not.exist(err);
res.body.should.eql({
error: 'forbidden',
reason: 'cannot modify pre-existing version: 0.0.2'
});
done();
});
});
});
describe('forcePublishWithScope = true', function () {
it('should publish without scope 403, addPackageAndDist()', function (done) {
mm(config, 'forcePublishWithScope', false);
var pkg = utils.getPackage('publicpublishmodule', '0.0.2');
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(403, done);
});
it('should admin publish without scope ok, addPackageAndDist()', function (done) {
mm(config, 'forcePublishWithScope', false);
var pkg = utils.getPackage('publicpublishmodule1', '0.0.4', utils.admin);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, done);
});
});
});
describe('PUT /:name/-rev/:rev removeWithVersions', function () {
var withoutScopeRev;
before(function (done) {
mm(config, 'enablePrivate', false);
mm(config, 'forcePublishWithScope', false);
var pkg = utils.getPackage('publicremovemodule', '0.0.1', utils.otherUser);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(201, function (err, res) {
should.not.exist(err);
res.body.should.have.keys('ok', 'rev');
res.body.ok.should.equal(true);
pkg = utils.getPackage('publicremovemodule', '0.0.2', utils.otherUser);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(201, function (err, res) {
should.not.exist(err);
withoutScopeRev = res.body.rev;
done();
});
});
});
it('should remove with version ok', function (done) {
mm.empty(Module, 'removeByNameAndVersions');
mm.empty(Module, 'removeTagsByIds');
request(app)
.put('/publicremovemodule/-rev/' + withoutScopeRev)
.set('authorization', utils.otherUserAuth)
.send({
versions: {
'0.0.1': {}
}
})
.expect(201, done);
});
it('should no auth user remove 403', function (done) {
mm.empty(Module, 'removeByNameAndVersions');
mm.empty(Module, 'removeTagsByIds');
request(app)
.put('/publicremovemodule/-rev/' + withoutScopeRev)
.set('authorization', utils.secondUserAuth)
.send({
versions: {
'0.0.1': {}
}
})
.expect(403, done);
});
it('should admin remove ok', function (done) {
mm.empty(Module, 'removeByNameAndVersions');
mm.empty(Module, 'removeTagsByIds');
request(app)
.put('/publicremovemodule/-rev/' + withoutScopeRev)
.set('authorization', utils.adminAuth)
.send({
versions: {
'0.0.1': {}
}
})
.expect(201, done);
});
describe('forcePublishWithScope = true', function () {
var withScopeRev;
before(function (done) {
mm(config, 'enablePrivate', false);
mm(config, 'forcePublishWithScope', true);
var pkg = utils.getPackage('@cnpm/publicremovemodule', '0.0.1', utils.otherUser);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(201, function (err, res) {
should.not.exist(err);
res.body.should.have.keys('ok', 'rev');
res.body.ok.should.equal(true);
pkg = utils.getPackage('@cnpm/publicremovemodule', '0.0.2', utils.otherUser);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(201, function (err, res) {
should.not.exist(err);
withScopeRev = res.body.rev;
done();
});
});
});
it('should remove without scope 403', function (done) {
mm(config, 'forcePublishWithScope', true);
mm.empty(Module, 'removeByNameAndVersions');
mm.empty(Module, 'removeTagsByIds');
request(app)
.put('/publicremovemodule/-rev/' + withoutScopeRev)
.set('authorization', utils.otherUserAuth)
.send({
versions: {
'0.0.1': {}
}
})
.expect(403, done);
});
it('should admin remove without scope ok', function (done) {
mm(config, 'forcePublishWithScope', true);
mm.empty(Module, 'removeByNameAndVersions');
mm.empty(Module, 'removeTagsByIds');
request(app)
.put('/publicremovemodule/-rev/' + withoutScopeRev)
.set('authorization', utils.adminAuth)
.send({
versions: {
'0.0.1': {}
}
})
.expect(201, done);
});
it('should remove with scope ok', function (done) {
mm(config, 'forcePublishWithScope', true);
mm.empty(Module, 'removeByNameAndVersions');
mm.empty(Module, 'removeTagsByIds');
request(app)
.put('/@cnpm/publicremovemodule/-rev/' + withScopeRev)
.set('authorization', utils.otherUserAuth)
.send({
versions: {
'0.0.1': {}
}
})
.expect(201, done);
});
it('should admin remove with scope ok', function (done) {
mm(config, 'forcePublishWithScope', true);
mm.empty(Module, 'removeByNameAndVersions');
mm.empty(Module, 'removeTagsByIds');
request(app)
.put('/@cnpm/publicremovemodule/-rev/' + withScopeRev)
.set('authorization', utils.adminAuth)
.send({
versions: {
'0.0.1': {}
}
})
.expect(201, done);
});
});
});
describe('DELETE /:name/download/:filename/-rev/:rev', function () {
var withoutScopeRev;
beforeEach(function () {
mm(config, 'enablePrivate', false);
mm(config, 'forcePublishWithScope', false);
});
beforeEach(function (done) {
mm(config, 'enablePrivate', false);
mm(config, 'forcePublishWithScope', false);
var pkg = utils.getPackage('public-test-delete-download-module', '0.1.9', utils.otherUser);
request(app)
.put('/' + pkg.name)
.set('content-type', 'application/json')
.set('authorization', utils.otherUserAuth)
.send(pkg)
.end(function (err, res) {
should.not.exist(err);
if (res.body.rev) {
withoutScopeRev = res.body.rev;
}
done();
});
});
it('should delete 403 when auth error', function (done) {
request(app)
.del('/public-test-delete-download-module/download/public-test-delete-download-module-0.1.9.tgz/-rev/' + withoutScopeRev)
.set('authorization', utils.secondUserAuth)
.expect(403, done);
});
it('should delete file ok', function (done) {
request(app)
.del('/public-test-delete-download-module/download/public-test-delete-download-module-0.1.9.tgz/-rev/' + withoutScopeRev)
.set('authorization', utils.otherUserAuth)
.expect(200, done);
});
it('should admin delete file ok', function (done) {
request(app)
.del('/public-test-delete-download-module/download/public-test-delete-download-module-0.1.9.tgz/-rev/' + withoutScopeRev)
.set('authorization', utils.adminAuth)
.expect(200, done);
});
describe('forcePublishWithScope = true', function () {
var withScopeRev;
beforeEach(function () {
mm(config, 'enablePrivate', false);
mm(config, 'forcePublishWithScope', true);
});
beforeEach(function (done) {
mm(config, 'enablePrivate', false);
mm(config, 'forcePublishWithScope', true);
var pkg = utils.getPackage('@cnpm/public-test-delete-download-module', '0.1.9', utils.otherUser);
request(app)
.put('/' + pkg.name)
.set('content-type', 'application/json')
.set('authorization', utils.otherUserAuth)
.send(pkg)
.end(function (err, res) {
should.not.exist(err);
if (res.body.rev) {
withScopeRev = res.body.rev;
}
done();
});
});
it('should delete file without scope 403', function (done) {
request(app)
.del('/public-test-delete-download-module/download/public-test-delete-download-module-0.1.9.tgz/-rev/' + withoutScopeRev)
.set('authorization', utils.otherUserAuth)
.expect(403, done);
});
it('should admin delete file without scope ok', function (done) {
request(app)
.del('/public-test-delete-download-module/download/public-test-delete-download-module-0.1.9.tgz/-rev/' + withoutScopeRev)
.set('authorization', utils.adminAuth)
.expect(200, done);
});
it('should delete file with scope ok', function (done) {
request(app)
.del('/@cnpm/public-test-delete-download-module/download/@cnpm/public-test-delete-download-module-0.1.9.tgz/-rev/' + withScopeRev)
.set('authorization', utils.otherUserAuth)
.expect(200, done);
});
it('should admin delete file with scope ok', function (done) {
request(app)
.del('/@cnpm/public-test-delete-download-module/download/@cnpm/public-test-delete-download-module-0.1.9.tgz/-rev/' + withScopeRev)
.set('authorization', utils.adminAuth)
.expect(200, done);
});
});
});
describe('PUT /:name/:tag updateTag()', function () {
it('should create new tag ok', function (done) {
request(app)
.put('/publictestmodule/newtag')
.set('content-type', 'application/json')
.set('authorization', utils.otherUserAuth)
.send('"0.0.1"')
.expect(201)
.expect({"ok":true}, done);
});
it('shold update tag not maintainer 403', function (done) {
request(app)
.put('/publictestmodule/newtag')
.set('content-type', 'application/json')
.set('authorization', utils.secondUserAuth)
.send('"0.0.1"')
.expect(403, done);
});
it('should admin update tag ok', function (done) {
request(app)
.put('/publictestmodule/newtag')
.set('content-type', 'application/json')
.set('authorization', utils.adminAuth)
.send('"0.0.1"')
.expect(201, done);
});
});
describe('DELETE /:name/-rev/:rev', function () {
describe('remove all modules by name', function () {
beforeEach(function () {
mm(config, 'enablePrivate', false);
mm(config, 'forcePublishWithScope', false);
});
before(function (done) {
mm(config, 'enablePrivate', false);
mm(config, 'forcePublishWithScope', false);
var pkg = utils.getPackage('public-remove-all-module', '0.0.1', utils.otherUser);
request(app)
.put('/public-remove-all-module')
.set('content-type', 'application/json')
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(201, done);
});
it('shold fail when user not maintainer', function (done) {
request(app)
.del('/public-remove-all-module/-rev/1')
.set('authorization', utils.secondUserAuth)
.expect(403, function (err, res) {
should.not.exist(err);
res.body.should.eql({
error: 'forbidden user',
reason: 'cnpmjstest102 not authorized to modify public-remove-all-module'
});
done();
});
});
it('shold maintainer remove ok', function (done) {
mm.empty(Module, 'removeByName');
mm.empty(Module, 'removeTags');
request(app)
.del('/public-remove-all-module/-rev/1')
.set('authorization', utils.otherUserAuth)
.expect(200, function (err, res) {
should.not.exist(err);
should.not.exist(res.headers['set-cookie']);
done();
});
});
it('shold admin remove ok', function (done) {
mm.empty(Module, 'removeByName');
mm.empty(Module, 'removeTags');
request(app)
.del('/public-remove-all-module/-rev/1')
.set('authorization', utils.adminAuth)
.expect(200, function (err, res) {
should.not.exist(err);
should.not.exist(res.headers['set-cookie']);
done();
});
});
describe('forcePublishWithScope = true', function () {
before(function (done) {
mm(config, 'enablePrivate', false);
mm(config, 'forcePublishWithScope', true);
var pkg = utils.getPackage('@cnpm/public-remove-all-module', '0.0.1', utils.otherUser);
request(app)
.put('/@cnpm/public-remove-all-module')
.set('content-type', 'application/json')
.set('authorization', utils.otherUserAuth)
.send(pkg)
.expect(201, done);
});
it('should fail when user remove module without scope', function (done) {
mm(config, 'forcePublishWithScope', true);
request(app)
.del('/public-remove-all-module/-rev/1')
.set('authorization', utils.otherUserAuth)
.expect(403, done);
});
it('shold admin remove module without scope ok', function (done) {
mm(config, 'forcePublishWithScope', true);
mm.empty(Module, 'removeByName');
mm.empty(Module, 'removeTags');
request(app)
.del('/public-remove-all-module/-rev/1')
.set('authorization', utils.adminAuth)
.expect(200, done);
});
it('shold maintainer remove ok', function (done) {
mm(config, 'forcePublishWithScope', true);
mm.empty(Module, 'removeByName');
mm.empty(Module, 'removeTags');
request(app)
.del('/@cnpm/public-remove-all-module/-rev/1')
.set('authorization', utils.otherUserAuth)
.expect(200, function (err, res) {
should.not.exist(err);
should.not.exist(res.headers['set-cookie']);
done();
});
});
it('shold admin remove ok', function (done) {
mm(config, 'forcePublishWithScope', true);
mm.empty(Module, 'removeByName');
mm.empty(Module, 'removeTags');
request(app)
.del('/@cnpm/public-remove-all-module/-rev/1')
.set('authorization', utils.adminAuth)
.expect(200, function (err, res) {
should.not.exist(err);
should.not.exist(res.headers['set-cookie']);
done();
});
});
});
});
});
});

View File

@@ -0,0 +1,278 @@
/**!
* 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';
/**
* Module dependencies.
*/
var should = require('should');
var request = require('supertest');
var mm = require('mm');
var config = require('../../../../config');
var app = require('../../../../servers/registry');
var utils = require('../../../utils');
var Module = require('../../../../proxy/module');
describe('controllers/registry/module/scope_package.test.js', function () {
var pkgname = '@cnpm/test-scope-package';
var pkgURL = '/@' + encodeURIComponent(pkgname.substring(1));
before(function (done) {
app = app.listen(0, function () {
// add scope package
var pkg = utils.getPackage(pkgname, '0.0.1', utils.admin);
request(app)
.put(pkgURL)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, function (err) {
should.not.exist(err);
pkg = utils.getPackage(pkgname, '0.0.2', utils.admin);
// publish 0.0.2
request(app.listen())
.put(pkgURL)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, done);
});
});
});
beforeEach(function () {
mm(config, 'scopes', ['@cnpm', '@cnpmtest']);
});
afterEach(mm.restore);
it('should get 404 when do not support scope', function (done) {
mm(config, 'scopes', []);
request(app)
.get('/@invalid/test')
.expect(404, done);
});
it('should get 400 when scope not match', function (done) {
request(app)
.get('/@invalid/test')
.expect(404, done);
});
it('should get scope package info: /@scope%2Fname', function (done) {
request(app)
.get(pkgURL)
.expect(200, function (err, res) {
should.not.exist(err);
var pkg = res.body;
pkg.name.should.equal(pkgname);
pkg.versions.should.have.keys('0.0.1', '0.0.2');
pkg['dist-tags'].latest.should.equal('0.0.2');
pkg.versions['0.0.1'].name.should.equal(pkgname);
pkg.versions['0.0.1'].dist.tarball
.should.containEql('/@cnpm/test-scope-package/download/@cnpm/test-scope-package-0.0.1.tgz');
done();
});
});
it('should get scope package info: /@scope/name', function (done) {
request(app.listen())
.get('/' + pkgname)
.expect(200, function (err, res) {
should.not.exist(err);
var pkg = res.body;
pkg.name.should.equal(pkgname);
pkg.versions.should.have.keys('0.0.1', '0.0.2');
pkg['dist-tags'].latest.should.equal('0.0.2');
pkg.versions['0.0.1'].name.should.equal(pkgname);
pkg.versions['0.0.1'].dist.tarball
.should.containEql('/@cnpm/test-scope-package/download/@cnpm/test-scope-package-0.0.1.tgz');
done();
});
});
it('should get scope package info: /%40scope%2Fname', function (done) {
request(app)
.get('/' + encodeURIComponent(pkgname))
.expect(200, function (err, res) {
should.not.exist(err);
var pkg = res.body;
pkg.name.should.equal(pkgname);
pkg.versions.should.have.keys('0.0.1', '0.0.2');
pkg['dist-tags'].latest.should.equal('0.0.2');
pkg.versions['0.0.1'].name.should.equal(pkgname);
pkg.versions['0.0.1'].dist.tarball
.should.containEql('/@cnpm/test-scope-package/download/@cnpm/test-scope-package-0.0.1.tgz');
done();
});
});
it('should get scope package with version', function (done) {
request(app)
.get('/' + pkgname + '/0.0.1')
.expect(200, function (err, res) {
should.not.exist(err);
var pkg = res.body;
pkg.name.should.equal(pkgname);
pkg.version.should.equal('0.0.1');
pkg.dist.tarball
.should.containEql('/@cnpm/test-scope-package/download/@cnpm/test-scope-package-0.0.1.tgz');
done();
});
});
it('should get scope package with tag', function (done) {
request(app)
.get('/' + pkgname + '/latest')
.expect(200, function (err, res) {
should.not.exist(err);
var pkg = res.body;
pkg.name.should.equal(pkgname);
pkg.version.should.equal('0.0.2');
pkg.dist.tarball
.should.containEql('/@cnpm/test-scope-package/download/@cnpm/test-scope-package-0.0.2.tgz');
done();
});
});
it('should download work', function (done) {
request(app)
.get('/@cnpm/test-scope-package/download/@cnpm/test-scope-package-0.0.2.tgz')
.expect(200, done);
});
describe('support adaptScope', function () {
before(function (done) {
var pkg = utils.getPackage('test-default-scope-package', '0.0.1', utils.admin);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, done);
});
describe('/@:scope/:name', function () {
it('should adapt /@cnpm/test-default-scope-package => /test-default-scope-package', function (done) {
mm(config, 'adaptScope', true);
request(app)
.get('/@cnpm/test-default-scope-package')
.expect(200, function (err, res) {
should.not.exist(err);
var pkg = res.body;
pkg._id.should.equal('@cnpm/test-default-scope-package');
pkg.name.should.equal('@cnpm/test-default-scope-package');
pkg.versions.should.have.keys('0.0.1');
pkg['dist-tags'].latest.should.equal('0.0.1');
pkg.versions['0.0.1'].name.should.equal('@cnpm/test-default-scope-package');
pkg.versions['0.0.1']._id.should.equal('@cnpm/test-default-scope-package@0.0.1');
pkg.versions['0.0.1'].dist.tarball
.should.containEql('/test-default-scope-package/download/test-default-scope-package-0.0.1.tgz');
done();
});
});
it('should adapt /@cnpmtest/test-default-scope-package => /test-default-scope-package', function (done) {
mm(config, 'adaptScope', true);
request(app)
.get('/@cnpmtest/test-default-scope-package')
.expect(200, function (err, res) {
should.not.exist(err);
var pkg = res.body;
pkg._id.should.equal('@cnpmtest/test-default-scope-package');
pkg.name.should.equal('@cnpmtest/test-default-scope-package');
pkg.versions.should.have.keys('0.0.1');
pkg['dist-tags'].latest.should.equal('0.0.1');
pkg.versions['0.0.1'].name.should.equal('@cnpmtest/test-default-scope-package');
pkg.versions['0.0.1']._id.should.equal('@cnpmtest/test-default-scope-package@0.0.1');
pkg.versions['0.0.1'].dist.tarball
.should.containEql('/test-default-scope-package/download/test-default-scope-package-0.0.1.tgz');
done();
});
});
it('should not adapt when adaptScope is false', function (done) {
mm(config, 'adaptScope', false);
request(app)
.get('/@cnpm/test-default-scope-package')
.expect(404, done);
});
it('should 404 when pkg not exists', function (done) {
mm(config, 'adaptScope', true);
request(app)
.get('/@cnpm/test-default-scope-package-not-exists')
.expect(404, done);
});
it('should show() 404 when adapt package is not private package', function (done) {
var getByTag = Module.getByTag;
mm(Module, 'getByTag', function* (name, tag) {
var pkg = yield getByTag.call(Module, name, tag);
pkg && delete pkg.package._publish_on_cnpm;
return pkg;
});
mm(config, 'adaptScope', true);
request(app)
.get('/@cnpm/test-default-scope-package')
.expect(404, done);
});
});
describe('/@:scope/:name/:tag', function () {
it('should adapt /@cnpm/test-default-scope-package/latest => /test-default-scope-package/latest', function (done) {
mm(config, 'adaptScope', true);
request(app)
.get('/@cnpm/test-default-scope-package/latest')
.expect(200, function (err, res) {
should.not.exist(err);
var pkg = res.body;
pkg.version.should.have.equal('0.0.1');
pkg.name.should.equal('@cnpm/test-default-scope-package');
pkg._id.should.equal('@cnpm/test-default-scope-package@0.0.1');
pkg.dist.tarball.should.containEql('/test-default-scope-package/download/test-default-scope-package-0.0.1.tgz');
done();
});
});
it('should not adapt when adaptScope is false', function (done) {
mm(config, 'adaptScope', false);
request(app)
.get('/@cnpm/test-default-scope-package/latest')
.expect(404, done);
});
it('should 404 when pkg not exists', function (done) {
mm(config, 'adaptScope', true);
request(app)
.get('/@cnpm/test-default-scope-package-not-exists/latest')
.expect(404, done);
});
it('should 404 when pkg version not exists', function (done) {
mm(config, 'adaptScope', true);
request(app)
.get('/@cnpm/test-default-scope-package-not-exists/1.0.0')
.expect(404, done);
});
it('should get() 404 when adapt package is not private package', function (done) {
var getByTag = Module.getByTag;
mm(Module, 'getByTag', function* (name, tag) {
var pkg = yield getByTag.call(Module, name, tag);
pkg && delete pkg.package._publish_on_cnpm;
return pkg;
});
mm(config, 'adaptScope', true);
request(app)
.get('/@cnpm/test-default-scope-package/latest')
.expect(404, done);
});
});
});
});

View File

@@ -20,14 +20,12 @@ var mm = require('mm');
var app = require('../../../servers/registry');
var user = require('../../../proxy/user');
var mysql = require('../../../common/mysql');
var config = require('../../../config');
var UserService = require('../../../services/user');
describe('controllers/registry/user.test.js', function () {
before(function (done) {
app.listen(0, done);
});
after(function (done) {
app.close(done);
app = app.listen(0, done);
});
afterEach(mm.restore);
@@ -88,91 +86,51 @@ describe('controllers/registry/user.test.js', function () {
});
it('should 409 when already exist', function (done) {
mm.data(user, 'get', {name: 'name'});
mm(user, 'get', function* () {
return {name: 'name'};
});
request(app)
.put('/-/user/org.couchdb.user:name')
.send({
name: 'name',
salt: 'salt',
password_sha: 'password_sha',
password: 'password',
email: 'email'
})
.expect(409, done);
});
it('should 500 when user.get error', function (done) {
mm.error(user, 'get', 'mock error');
mm(user, 'get', function* () {
throw new Error('mock User.get error');
});
request(app)
.put('/-/user/org.couchdb.user:name')
.send({
name: 'name',
salt: 'salt',
password_sha: 'password_sha',
password: 'password',
email: 'email'
})
.expect(500, done);
});
it('should 201 when user.add ok', function (done) {
mm.empty(user, 'get');
mm.data(user, 'add', {rev: '1-123'});
mm(user, 'get', function* () {
return null;
});
mm(user, 'add', function* () {
return {rev: '1-123'};
});
request(app)
.put('/-/user/org.couchdb.user:name')
.send({
name: 'name',
salt: 'salt',
password_sha: 'password_sha',
password: 'password',
email: 'email'
})
.expect(201, done);
});
});
describe('POST /_session', function () {
it('should 500 auth error by user.auth', function (done) {
mm.error(user, 'auth', 'mock error');
request(app)
.post('/_session')
.send({
name: 'name',
password: '123'
})
.expect(500, done);
});
it('should 401 auth fail by user.auth', function (done) {
mm.empty(user, 'auth');
request(app)
.post('/_session')
.send({
name: 'name',
password: '123'
})
.expect(401, done);
});
it('should 200 auth pass by user.auth', function (done) {
mm.data(user, 'auth', {name: 'name'});
request(app)
.post('/_session')
.send({
name: 'name',
password: '123'
})
.expect(200)
.expect({
ok: true,
name: 'name',
roles: []
}, function (err, res) {
should.not.exist(err);
should.exist(res.headers['set-cookie']);
res.headers['set-cookie'].join(';').should.include('AuthSession=');
done();
});
});
});
describe('PUT /-/user/:name/-rev/:rev', function () {
it('should 404 when without a name', function (done) {
request(app)
@@ -227,4 +185,184 @@ describe('controllers/registry/user.test.js', function () {
.expect(201, done);
});
});
describe('config.customUserSerivce = true', function () {
beforeEach(function () {
mm(config, 'customUserService', true);
});
it('should 422 when password missing', function (done) {
request(app)
.put('/-/user/org.couchdb.user:cnpmjstest10-not-exists')
.send({
name: 'cnpmjstest10-not-exists',
password: '',
email: 'cnpmjstest10@cnpmjs.org'
})
.expect({
error: 'paramError',
reason: 'params missing, name, email or password missing.'
})
.expect(422, done);
});
it('should 201 login success', function (done) {
request(app)
.put('/-/user/org.couchdb.user:cnpmjstest10')
.send({
name: 'cnpmjstest10',
password: 'cnpmjstest10',
email: 'cnpmjstest10@cnpmjs.org'
})
.expect(201, function (err, res) {
should.not.exist(err);
res.body.should.have.keys('ok', 'id', 'rev');
res.body.id.should.equal('org.couchdb.user:cnpmjstest10');
res.body.rev.should.match(/\d+\-cnpmjstest10/);
res.body.ok.should.equal(true);
done();
});
});
it('should 401 login fail', function (done) {
request(app)
.put('/-/user/org.couchdb.user:cnpmjstest10-not-exists')
.send({
name: 'cnpmjstest10-not-exists',
password: 'cnpmjstest10',
email: 'cnpmjstest10@cnpmjs.org'
})
.expect({
error: 'unauthorized',
reason: 'Login fail, please check your login name and password'
})
.expect(401, done);
});
});
describe('config.customUserService = true', function () {
beforeEach(function () {
mm(config, 'customUserService', true);
});
afterEach(mm.restore);
it('should show custom user info: admin', function (done) {
mm(UserService, 'get', function* () {
return {
login: 'mock_custom_user',
email: 'mock_custom_user@cnpmjs.org',
name: 'mock_custom_user fullname',
avatar_url: 'avatar_url',
html_url: 'html_url',
im_url: '',
site_admin: true,
scopes: ['@test-user-scope']
};
});
request(app)
.get('/-/user/org.couchdb.user:mock_custom_user')
.expect(200, function (err, res) {
should.not.exist(err);
var user = res.body;
delete user._cnpm_meta.gmt_create;
delete user._cnpm_meta.gmt_modified;
delete user._cnpm_meta.id;
delete user.date;
user.should.eql({
_id: 'org.couchdb.user:mock_custom_user',
_rev: '1-mock_custom_user',
name: 'mock_custom_user',
email: 'mock_custom_user@cnpmjs.org',
type: 'user',
roles: [],
// date: '2014-07-28T16:46:36.000Z',
avatar: 'avatar_url',
fullname: 'mock_custom_user fullname',
homepage: 'html_url',
_cnpm_meta:
{
// id: 4,
npm_user: false,
custom_user: true,
// gmt_create: '2014-07-28T16:46:36.000Z',
// gmt_modified: '2014-07-28T16:46:36.000Z',
admin: true,
scopes: [ '@test-user-scope' ] }
});
done();
});
});
it('should show custom user info: not admin', function (done) {
mm(UserService, 'get', function* () {
return {
login: 'mock_custom_not_admin_user',
email: 'mock_custom_not_admin_user@cnpmjs.org',
name: 'mock_custom_not_admin_user fullname',
avatar_url: 'avatar_url',
html_url: 'html_url',
im_url: '',
site_admin: false,
scopes: ['@test-user-scope']
};
});
request(app)
.get('/-/user/org.couchdb.user:mock_custom_not_admin_user')
.expect(200, function (err, res) {
should.not.exist(err);
var user = res.body;
delete user._cnpm_meta.gmt_create;
delete user._cnpm_meta.gmt_modified;
delete user._cnpm_meta.id;
delete user.date;
user.should.eql({
_id: 'org.couchdb.user:mock_custom_not_admin_user',
_rev: '1-mock_custom_not_admin_user',
name: 'mock_custom_not_admin_user',
email: 'mock_custom_not_admin_user@cnpmjs.org',
type: 'user',
roles: [],
// date: '2014-07-28T16:46:36.000Z',
avatar: 'avatar_url',
fullname: 'mock_custom_not_admin_user fullname',
homepage: 'html_url',
_cnpm_meta:
{
// id: 5,
npm_user: false,
custom_user: true,
// gmt_create: '2014-07-28T16:46:36.000Z',
// gmt_modified: '2014-07-28T16:46:36.000Z',
admin: false,
scopes: [ '@test-user-scope' ] }
});
done();
});
});
it('should show error json when userSerive.auth throw error', function (done) {
mm(UserService, 'auth', function* () {
var err = new Error('mock user service auth error, please visit http://ooxx.net/user to sigup first');
err.name = 'UserSeriveAuthError';
err.status = 401;
throw err;
});
request(app)
.put('/-/user/org.couchdb.user:cnpmjstest10')
.send({
name: 'cnpmjstest10',
password: 'cnpmjstest10',
email: 'cnpmjstest10@cnpmjs.org'
})
.expect({
error: 'UserSeriveAuthError',
reason: 'mock user service auth error, please visit http://ooxx.net/user to sigup first'
})
.expect(401, done);
});
});
});

View File

@@ -0,0 +1,162 @@
/**!
* cnpmjs.org - test/contributors/registry/user_package.test.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var should = require('should');
var request = require('supertest');
var pedding = require('pedding');
var co = require('co');
var app = require('../../../servers/registry');
var SyncModuleWorker = require('../../../proxy/sync_module_worker');
var NpmModuleMaintainer = require('../../../proxy/npm_module_maintainer');
var utils = require('../../utils');
describe('contributors/registry/user_package.test.js', function () {
before(function (done) {
co(function* () {
yield* NpmModuleMaintainer.removeAll('pedding');
})(function (err) {
should.not.exist(err);
// sync pedding
var worker = new SyncModuleWorker({
name: 'pedding',
noDep: true,
});
worker.start();
worker.on('end', function () {
var pkg = utils.getPackage('test-user-package-module', '0.0.1', utils.otherAdmin2);
request(app)
.put('/' + pkg.name)
.set('authorization', utils.otherAdmin2Auth)
.send(pkg)
.expect(201, done);
});
});
});
describe('listOne()', function () {
it('should return one user\'s all package names', function (done) {
request(app.listen())
.get('/-/by-user/fengmk2')
.expect(200)
.expect({
fengmk2: [
'pedding',
]
}, done);
});
it('should return {} when user not exists', function (done) {
request(app.listen())
.get('/-/by-user/user-not-exists')
.expect(200)
.expect({}, done);
});
});
describe('listMulti()', function () {
it('should return two exists user\'s all package names', function (done) {
done = pedding(2, done);
request(app.listen())
.get('/-/by-user/' + encodeURIComponent('fengmk2|dead-horse'))
.expect(200)
.expect({
fengmk2: [
'pedding',
],
'dead-horse': [
'pedding'
]
}, done);
request(app.listen())
.get('/-/by-user/fengmk2|dead-horse')
.expect(200)
.expect({
fengmk2: [
'pedding',
],
'dead-horse': [
'pedding'
]
}, done);
});
it('should return some exists user\'s all package names', function (done) {
done = pedding(2, done);
request(app.listen())
.get('/-/by-user/' + encodeURIComponent('fengmk2|user-not-exists'))
.expect(200)
.expect({
fengmk2: [
'pedding'
]
}, done);
request(app.listen())
.get('/-/by-user/' + utils.otherAdmin2 + '|fengmk2|user-not-exists|')
.expect(200)
.expect({
cnpmjstestAdmin2: [
'test-user-package-module'
],
fengmk2: [
'pedding'
]
}, done);
});
it('should return {} when users not exists', function (done) {
request(app.listen())
.get('/-/by-user/user-not-exists1|user-not-exists2')
.expect(200)
.expect({}, done);
});
it('should return {} when first user name empty', function (done) {
done = pedding(2, done);
request(app.listen())
.get('/-/by-user/|user-not-exists2')
.expect(200)
.expect({}, done);
request(app.listen())
.get('/-/by-user/|')
.expect(200)
.expect({}, done);
});
it('should return 200 when users length equal limit count', function (done) {
request(app.listen())
.get('/-/by-user/n1|n2|n3|n4|n5|n6|n7|n8|n9|n10|n11|n12|n13|n14|n15|n16|n17|n18|n19|n20')
.expect(200, done);
});
it('should return 400 when users reach limit count', function (done) {
request(app.listen())
.get('/-/by-user/n1|n2|n3|n4|n5|n6|n7|n8|n9|n10|n11|n12|n13|n14|n15|n16|n17|n18|n19|n20|n21')
.expect(400)
.expect({
error: 'bad_request',
reason: 'reach max user names limit, must <= 20 user names'
}, done);
});
});
});

View File

@@ -27,8 +27,8 @@ var webApp = require('../../servers/web');
describe('controllers/sync.test.js', function () {
before(function (done) {
done = pedding(2, done);
registryApp.listen(0, done);
webApp.listen(0, done);
registryApp = registryApp.listen(0, done);
webApp = webApp.listen(0, done);
});
afterEach(mm.restore);
@@ -43,14 +43,14 @@ describe('controllers/sync.test.js', function () {
it('should sync as publish success', function (done) {
request(registryApp)
.del('/utility/-rev/123')
.del('/pedding/-rev/123')
.set('authorization', baseauth)
.end(function (err, res) {
should.not.exist(err);
mm.data(Npm, 'get', require(path.join(fixtures, 'utility.json')));
request(registryApp)
.put('/utility/sync?publish=true&nodeps=true')
.put('/pedding/sync?publish=true&nodeps=true')
.set('authorization', baseauth)
.end(function (err, res) {
should.not.exist(err);
@@ -75,7 +75,7 @@ describe('controllers/sync.test.js', function () {
it('should sync through web success', function (done) {
mm.data(Npm, 'get', require(path.join(fixtures, 'utility.json')));
request(webApp)
.put('/sync/utility')
.put('/sync/pedding')
.end(function (err, res) {
should.not.exist(err);
res.body.should.have.keys('ok', 'logId');
@@ -87,7 +87,7 @@ describe('controllers/sync.test.js', function () {
it('should sync through registry success', function (done) {
mm.data(Npm, 'get', require(path.join(fixtures, 'utility.json')));
request(registryApp)
.put('/utility/sync')
.put('/pedding/sync')
.set('authorization', baseauth)
.end(function (err, res) {
should.not.exist(err);
@@ -100,7 +100,7 @@ describe('controllers/sync.test.js', function () {
it('should get sync log', function (done) {
done = pedding(2, done);
request(registryApp)
.get('/utility/sync/log/' + logIdRegistry)
.get('/pedding/sync/log/' + logIdRegistry)
.end(function (err, res) {
should.not.exist(err);
res.body.should.have.keys('ok', 'log');
@@ -108,7 +108,7 @@ describe('controllers/sync.test.js', function () {
});
request(webApp)
.get('/sync/utility/log/' + logIdWeb)
.get('/sync/pedding/log/' + logIdWeb)
.end(function (err, res) {
should.not.exist(err);
res.body.should.have.keys('ok', 'log');
@@ -116,4 +116,12 @@ describe('controllers/sync.test.js', function () {
});
});
});
describe('scope package', function () {
it('should sync scope package not found', function (done) {
request(webApp)
.put('/sync/@cnpm/not-exists-package')
.expect(404, done);
});
});
});

View File

@@ -27,11 +27,6 @@ describe('controllers/total.test.js', function () {
registryApp.listen(0, done);
webApp.listen(0, done);
});
after(function (done) {
done = pedding(2, done);
registryApp.close(done);
webApp.close(done);
});
describe('GET / in registry', function () {
it('should return total info', function (done) {

View File

@@ -0,0 +1,108 @@
/*!
* cnpmjs.org - test/controllers/web/badge.test.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var should = require('should');
var request = require('supertest');
var mm = require('mm');
var path = require('path');
var pedding = require('pedding');
var mysql = require('../../../common/mysql');
var app = require('../../../servers/web');
var registry = require('../../../servers/registry');
var pkg = require('../../../controllers/web/package');
var utils = require('../../utils');
var config = require('../../../config');
var fixtures = path.join(path.dirname(path.dirname(__dirname)), 'fixtures');
describe('controllers/web/badge.test.js', function () {
before(function (done) {
done = pedding(2, done);
registry = registry.listen(0, done);
app = app.listen(0, done);
});
afterEach(mm.restore);
describe('GET /badge/v/:name.svg', function () {
it('should show blue version on >=1.0.0 when package exists', function (done) {
var pkg = utils.getPackage('badge-test-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/v/badge-test-module.svg?style=flat-square')
.expect('Location', 'https://img.shields.io/badge/cnpm-1.0.1-blue.svg?style=flat-square')
.expect(302, done);
});
});
it('should support 1.0.0-beta1', function (done) {
var pkg = utils.getPackage('badge-test-module', '1.0.0-beta1', utils.admin);
request(registry)
.put('/' + pkg.name)
.set('authorization', utils.adminAuth)
.send(pkg)
.end(function (err) {
should.not.exists(err);
request(app)
.get('/badge/v/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(302, done);
});
});
it('should show green version on <1.0.0 & >=0.1.0 when package exists', function (done) {
var pkg = utils.getPackage('badge-test-module', '0.1.0', utils.admin);
request(registry)
.put('/' + pkg.name)
.set('authorization', utils.adminAuth)
.send(pkg)
.end(function (err) {
should.not.exists(err);
request(app)
.get('/badge/v/badge-test-module.svg?style=flat-square')
.expect('Location', 'https://img.shields.io/badge/cnpm-0.1.0-green.svg?style=flat-square')
.expect(302, done);
});
});
it('should show green version on <0.1.0 & >=0.0.0 when package exists', function (done) {
var pkg = utils.getPackage('badge-test-module', '0.0.0', utils.admin);
request(registry)
.put('/' + pkg.name)
.set('authorization', utils.adminAuth)
.send(pkg)
.end(function (err) {
should.not.exists(err);
request(app)
.get('/badge/v/badge-test-module.svg?style=flat-square')
.expect('Location', 'https://img.shields.io/badge/cnpm-0.0.0-red.svg?style=flat-square')
.expect(302, done);
});
});
it('should show invalid when package not exists', function (done) {
request(app)
.get('/badge/v/badge-test-module-not-exists.svg?style=flat')
.expect('Location', 'https://img.shields.io/badge/cnpm-invalid-lightgrey.svg?style=flat')
.expect(302, done);
});
});
});

View File

@@ -18,16 +18,14 @@ var should = require('should');
var request = require('supertest');
var pedding = require('pedding');
var mm = require('mm');
var fs = require('fs');
var nfs = require('../../../common/nfs');
var app = require('../../../servers/web');
var Dist = require('../../../proxy/dist');
describe('controllers/web/dist.test.js', function () {
before(function (done) {
app.listen(0, done);
});
after(function (done) {
app.close(done);
app = app.listen(0, done);
});
afterEach(mm.restore);
@@ -46,7 +44,7 @@ describe('controllers/web/dist.test.js', function () {
.expect('Content-Type', 'text/html; charset=utf-8')
.expect(200, function (err, res) {
should.not.exist(err);
res.text.should.include('<title>Mirror index of http://nodejs.org/dist/</title>');
res.text.should.containEql('<title>Mirror index of http://nodejs.org/dist/</title>');
done();
});
});
@@ -63,10 +61,10 @@ describe('controllers/web/dist.test.js', function () {
.expect('Content-Type', 'text/html; charset=utf-8')
.expect(200, function (err, res) {
should.not.exist(err);
res.text.should.include('<title>Mirror index of http://nodejs.org/dist/v1.0.0/</title>');
res.text.should.include('<h1>Mirror index of <a target="_blank" href="http://nodejs.org/dist/v1.0.0/">http://nodejs.org/dist/v1.0.0/</a></h1>');
res.text.should.include('<a href="ooxx/">ooxx/</a> 02-May-2014 00:54 -\n');
res.text.should.include('<a href="foo.txt">foo.txt</a> 02-May-2014 00:54 1024\n');
res.text.should.containEql('<title>Mirror index of http://nodejs.org/dist/v1.0.0/</title>');
res.text.should.containEql('<h1>Mirror index of <a target="_blank" href="http://nodejs.org/dist/v1.0.0/">http://nodejs.org/dist/v1.0.0/</a></h1>');
res.text.should.containEql('<a href="ooxx/">ooxx/</a> 02-May-2014 00:54 -\n');
res.text.should.containEql('<a href="foo.txt">foo.txt</a> 02-May-2014 00:54 1024\n');
done();
});
});
@@ -83,46 +81,91 @@ describe('controllers/web/dist.test.js', function () {
.expect('Content-Type', 'text/html; charset=utf-8')
.expect(200, function (err, res) {
should.not.exist(err);
res.text.should.include('<title>Mirror index of http://nodejs.org/dist/</title>');
res.text.should.include('<h1>Mirror index of <a target="_blank" href="http://nodejs.org/dist/">http://nodejs.org/dist/</a></h1>');
res.text.should.include('<a href="npm/">npm/</a> 02-May-2014 00:54 -\n');
res.text.should.include('<a href="npm-versions.txt">npm-versions.txt</a> 02-May-2014 00:54 1676\n');
res.text.should.containEql('<title>Mirror index of http://nodejs.org/dist/</title>');
res.text.should.containEql('<h1>Mirror index of <a target="_blank" href="http://nodejs.org/dist/">http://nodejs.org/dist/</a></h1>');
res.text.should.containEql('<a href="npm/">npm/</a> 02-May-2014 00:54 -\n');
res.text.should.containEql('<a href="npm-versions.txt">npm-versions.txt</a> 02-May-2014 00:54 1676\n');
done();
});
});
});
describe('GET /dist/ files', function () {
it('should redirect to nfs url', function (done) {
it('should pipe txt', function (done) {
mm(Dist, 'getfile', function* () {
return {
name: 'foo.txt', size: 1024, date: '02-May-2014 00:54',
url: 'http://mock.com/dist/v0.10.28/SHASUMS.txt'
};
});
fs.writeFileSync(nfs._getpath('/dist/v0.10.28/SHASUMS.txt'), '6eff580cc8460741155d42ef1ef537961194443f');
request(app)
.get('/dist/v0.10.28/SHASUMS.txt')
.expect(302)
.expect('Location', 'http://mock.com/dist/v0.10.28/SHASUMS.txt', done);
.expect('Content-Type', 'text/plain; charset=utf-8')
.expect(200, function (err, res) {
should.not.exist(err);
should.not.exist(res.headers['Content-Disposition']);
done();
});
});
it('should GET /dist/npm-versions.txt redirect to nfs url', function (done) {
it('should pipe html', function (done) {
mm(Dist, 'getfile', function* () {
return {
name: 'foo.html', size: 1024, date: '02-May-2014 00:54',
url: 'http://mock.com/dist/v0.10.28/foo.html'
};
});
fs.writeFileSync(nfs._getpath('/dist/v0.10.28/foo.html'), '<p>hi</p>');
request(app)
.get('/dist/v0.10.28/foo.html')
.expect('Content-Type', 'text/html; charset=utf-8')
.expect('<p>hi</p>')
.expect(200, function (err, res) {
should.not.exist(err);
should.not.exist(res.headers['Content-Disposition']);
done();
});
});
it('should pipe json', function (done) {
mm(Dist, 'getfile', function* () {
return {
name: 'foo.json', date: '02-May-2014 00:54',
url: 'http://mock.com/dist/v0.10.28/foo.json'
};
});
fs.writeFileSync(nfs._getpath('/dist/v0.10.28/foo.json'), '{}');
request(app)
.get('/dist/v0.10.28/foo.json')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect('{}')
.expect(200, function (err, res) {
should.not.exist(err);
should.not.exist(res.headers['Content-Disposition']);
done();
});
});
it('should GET /dist/npm-versions.tgz redirect to nfs url', function (done) {
mm(Dist, 'getfile', function* (fullname) {
fullname.should.equal('/npm-versions.txt');
fullname.should.equal('/npm-versions.tgz');
return {
name: 'npm-versions.txt', size: 1024, date: '02-May-2014 00:54',
url: 'http://mock.com/dist/npm-versions.txt'
url: 'http://mock.com/dist/npm-versions.tgz'
};
});
request(app)
.get('/dist/npm-versions.txt')
.get('/dist/npm-versions.tgz')
.expect(302)
.expect('Location', 'http://mock.com/dist/npm-versions.txt', done);
.expect('Location', 'http://mock.com/dist/npm-versions.tgz', done);
});
it('should download nfs file and send it', function (done) {
it('should download nfs txt file and send it', function (done) {
mm(Dist, 'getfile', function* () {
return {
name: 'foo.txt',
@@ -131,11 +174,43 @@ describe('controllers/web/dist.test.js', function () {
url: '/dist/v0.10.28/SHASUMS.txt'
};
});
fs.writeFileSync(nfs._getpath('/dist/v0.10.28/SHASUMS.txt'), '6eff580cc8460741155d42ef1ef537961194443f');
request(app)
.get('/dist/v0.10.28/SHASUMS.txt')
.expect(200)
.expect(/6eff580cc8460741155d42ef1ef537961194443f/, done);
});
it('should download nfs tgz file and send it', function (done) {
mm(Dist, 'getfile', function* () {
return {
name: 'foo.tgz',
size: 1264,
date: '02-May-2014 00:54',
url: '/dist/v0.10.28/foo.tgz'
};
});
fs.writeFileSync(nfs._getpath('/dist/v0.10.28/foo.tgz'), '6eff580cc8460741155d42ef1ef537961194443f');
request(app)
.get('/dist/v0.10.28/foo.tgz')
.expect('Content-Disposition', 'attachment; filename="foo.tgz"')
.expect(200, done);
});
it.skip('should download nfs no-ascii attachment file name', function (done) {
mm(Dist, 'getfile', function* () {
return {
name: '中文名.tgz',
size: 1264,
date: '02-May-2014 00:54',
url: '/dist/v0.10.28/foo.tgz'
};
});
fs.writeFileSync(nfs._getpath('/dist/v0.10.28/foo.tgz'), '6eff580cc8460741155d42ef1ef537961194443f');
request(app)
.get('/dist/v0.10.28/foo.tgz')
.expect('Content-Disposition', 'attachment; filename="%E4%B8%AD%E6%96%87%E5%90%8D.tgz"')
.expect(200, done);
});
});
});

View File

@@ -18,31 +18,31 @@ var should = require('should');
var request = require('supertest');
var mm = require('mm');
var path = require('path');
var pedding = require('pedding');
var mysql = require('../../../common/mysql');
var app = require('../../../servers/web');
var registry = require('../../../servers/registry');
var pkg = require('../../../controllers/web/package');
var SyncModuleWorker = require('../../../proxy/sync_module_worker');
var utils = require('../../utils');
var config = require('../../../config');
var fixtures = path.join(path.dirname(path.dirname(__dirname)), 'fixtures');
describe('controllers/web/package.test.js', function () {
var baseauth = 'Basic ' + new Buffer('cnpmjstest10:cnpmjstest10').toString('base64');
before(function (done) {
registry.listen(0, function () {
var pkg = require(path.join(fixtures, 'package_and_tgz.json'));
done = pedding(2, done);
registry = registry.listen(0, function () {
// name: mk2testmodule
var pkg = utils.getPackage('mk2testmodule', '0.0.1', utils.admin);
request(registry)
.put('/' + pkg.name)
.set('authorization', baseauth)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, function () {
app.listen(0, done);
});
.end(done);
});
});
after(function (done) {
app.close(done);
app = app.listen(0, done);
});
afterEach(mm.restore);
@@ -51,7 +51,7 @@ describe('controllers/web/package.test.js', function () {
it('should search with "m"', function (done) {
request(app)
.get('/_list/search/search?startkey="m"&limit=2')
.expect('content-type', 'application/json')
.expect('content-type', 'application/json; charset=utf-8')
.expect(200, function (err, res) {
should.not.exist(err);
res.body.should.have.keys('rows');
@@ -102,7 +102,7 @@ describe('controllers/web/package.test.js', function () {
.expect(/<th>Version<\/th>/, function (err, res) {
should.not.exist(err);
res.should.have.header('etag');
res.text.should.include('<meta charset="utf-8">');
res.text.should.containEql('<meta charset="utf-8">');
done();
});
});
@@ -214,4 +214,45 @@ describe('controllers/web/package.test.js', function () {
.expect(/Log/, done);
});
});
describe('unpublished package', function () {
before(function (done) {
var worker = new SyncModuleWorker({
name: ['tnpm'],
username: 'fengmk2'
});
worker.start();
worker.on('end', function () {
var names = worker.successes.concat(worker.fails);
names.sort();
names.should.eql(['tnpm']);
done();
});
});
it('should display unpublished info', function (done) {
request(app)
.get('/package/tnpm')
.expect(200)
.expect(/This package has been unpublished\./, done);
});
});
describe('GET /privates', function () {
it('should response no private packages', function (done) {
mm(config, 'scopes', []);
request(app)
.get('/privates')
.expect(/Can not found private package/)
.expect(200, done);
});
it('should response no private packages', function (done) {
request(app)
.get('/privates')
.expect(/Private packages in this registry/)
.expect(200, done);
});
});
});

View File

@@ -0,0 +1,177 @@
/**!
* cnpmjs.org - test/controllers/web/package/scope_package.test.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var should = require('should');
var request = require('supertest');
var pedding = require('pedding');
var mm = require('mm');
var config = require('../../../../config');
var registry = require('../../../../servers/registry');
var web = require('../../../../servers/web');
var utils = require('../../../utils');
var Module = require('../../../../proxy/module');
describe('controllers/web/package/scope_package.test.js', function () {
var pkgname = '@cnpm/test-web-scope-package';
var pkgURL = '/@' + encodeURIComponent(pkgname.substring(1));
before(function (done) {
done = pedding(2, done);
registry = registry.listen(0, function () {
// add scope package
var pkg = utils.getPackage(pkgname, '0.0.1', utils.admin);
request(registry)
.put(pkgURL)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, function (err) {
should.not.exist(err);
pkg = utils.getPackage(pkgname, '0.0.2', utils.admin);
// publish 0.0.2
request(registry)
.put(pkgURL)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, done);
});
});
web = web.listen(0, done);
});
beforeEach(function () {
mm(config, 'scopes', ['@cnpm', '@cnpmtest']);
});
afterEach(mm.restore);
it('should show scope package info page: /@scope%2Fname', function (done) {
request(web)
.get('/package' + pkgURL)
.expect(200, function (err, res) {
should.not.exist(err);
var body = res.text;
body.should.containEql('$ cnpm install @cnpm/test-web-scope-package');
body.should.containEql('/@cnpm/test-web-scope-package/download/@cnpm/test-web-scope-package-0.0.2.tgz');
done();
});
});
it('should show scope package info page: encodeURIComponent("/@scope/name")', function (done) {
request(web)
.get('/package/' + encodeURIComponent(pkgname))
.expect(200, function (err, res) {
should.not.exist(err);
var body = res.text;
body.should.containEql('$ cnpm install @cnpm/test-web-scope-package');
body.should.containEql('/@cnpm/test-web-scope-package/download/@cnpm/test-web-scope-package-0.0.2.tgz');
done();
});
});
it('should show scope package info page: /@scope/name', function (done) {
request(web)
.get('/package/' + pkgname)
.expect(200, function (err, res) {
should.not.exist(err);
var body = res.text;
body.should.containEql('$ cnpm install @cnpm/test-web-scope-package');
body.should.containEql('/@cnpm/test-web-scope-package/download/@cnpm/test-web-scope-package-0.0.2.tgz');
done();
});
});
it('should /package/@scope/name/ 404', function (done) {
request(web)
.get('/package/' + pkgname + '/')
.expect(404, done);
});
it('should show scope package with version: /@scope/name/0.0.2', function (done) {
request(web)
.get('/package/' + pkgname + '/0.0.2')
.expect(200, function (err, res) {
should.not.exist(err);
var body = res.text;
body.should.containEql('$ cnpm install @cnpm/test-web-scope-package');
body.should.containEql('/@cnpm/test-web-scope-package/download/@cnpm/test-web-scope-package-0.0.2.tgz');
done();
});
});
it('should /@scope/name redirect to /package/@scope/name', function (done) {
request(web)
.get('/' + pkgname)
.expect('Location', '/package/' + pkgname)
.expect(302, done);
});
describe('support adapt scope', function () {
before(function (done) {
var pkg = utils.getPackage('test-default-web-scope-package', '0.0.1', utils.admin);
request(registry)
.put('/' + pkg.name)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, done);
});
it('should adapt /@cnpm/test-default-web-scope-package => /test-default-web-scope-package', function (done) {
mm(config, 'adaptScope', true);
request(web)
.get('/package/@cnpm/test-default-web-scope-package')
.expect(200, function (err, res) {
should.not.exist(err);
var body = res.text;
body.should.containEql('@cnpm/test-default-web-scope-package');
body.should.containEql('/test-default-web-scope-package/download/test-default-web-scope-package-0.0.1.tgz');
done();
});
});
it('should not adapt /@cnpm123/test-default-web-scope-package', function (done) {
mm(config, 'adaptScope', true);
request(web)
.get('/package/@cnpm123/test-default-web-scope-package')
.expect(404, done);
});
it('should not adapt', function (done) {
mm(config, 'adaptScope', false);
request(web)
.get('/package/@cnpm/test-default-web-scope-package')
.expect(404, done);
});
it('should 404 when pkg not exists', function (done) {
mm(config, 'adaptScope', true);
request(web)
.get('/package/@cnpm/test-default-web-scope-package-not-exists')
.expect(404, done);
});
it('should 404 when pkg is not private package', function (done) {
var getByTag = Module.getByTag;
mm(Module, 'getByTag', function* (name, tag) {
var pkg = yield getByTag.call(Module, name, tag);
pkg && delete pkg.package._publish_on_cnpm;
return pkg;
});
mm(config, 'adaptScope', true);
request(web)
.get('/package/@cnpm/test-default-web-scope-package')
.expect(404, done);
});
});
});

View File

@@ -20,10 +20,7 @@ var app = require('../../../servers/web');
describe('controllers/web/user.test.js', function () {
before(function (done) {
app.listen(0, done);
});
after(function (done) {
app.close(done);
app = app.listen(0, done);
});
describe('GET /~:name', function (done) {
@@ -33,7 +30,7 @@ describe('controllers/web/user.test.js', function () {
.expect(200)
.expect('content-type', 'text/html; charset=utf-8')
.expect(/<div id="profile">/)
.expect(/Packages by /, done);
.expect(/Packages by/, done);
});
it('should get 404', function (done) {

16
test/fixtures/scope-package/.gitignore vendored Normal file
View File

@@ -0,0 +1,16 @@
coverage.html
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
node_modules
npm-debug.log
coverage/

View File

@@ -0,0 +1,4 @@
node_modules/
coverage/
.tmp/
.git/

95
test/fixtures/scope-package/.jshintrc vendored Normal file
View File

@@ -0,0 +1,95 @@
{
// JSHint Default Configuration File (as on JSHint website)
// See http://jshint.com/docs/ for more details
"maxerr" : 50, // {int} Maximum error before stopping
// Enforcing
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
"camelcase" : false, // true: Identifiers must be in camelCase
"curly" : true, // true: Require {} for every new block or scope
"eqeqeq" : true, // true: Require triple equals (===) for comparison
"forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty()
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
"indent" : false, // {int} Number of spaces to use for indentation
"latedef" : false, // true: Require variables/functions to be defined before being used
"newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
"noempty" : true, // true: Prohibit use of empty blocks
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
"plusplus" : false, // true: Prohibit use of `++` & `--`
"quotmark" : false, // Quotation mark consistency:
// false : do nothing (default)
// true : ensure whatever is used is consistent
// "single" : require single quotes
// "double" : require double quotes
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
"unused" : false, // true: Require all defined variables be used
"strict" : true, // true: Requires all functions run in ES5 Strict Mode
"trailing" : false, // true: Prohibit trailing whitespaces
"maxparams" : false, // {int} Max number of formal params allowed per function
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
"maxstatements" : false, // {int} Max number statements per function
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
"maxlen" : false, // {int} Max number of characters per line
// Relaxing
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
"boss" : true, // true: Tolerate assignments where comparisons would be expected
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
"eqnull" : false, // true: Tolerate use of `== null`
"es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
"esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`)
"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
// (ex: `for each`, multiple try/catch, function expression…)
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
"expr" : true, // true: Tolerate `ExpressionStatement` as Programs
"funcscope" : false, // true: Tolerate defining variables inside control statements"
"globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
"iterator" : false, // true: Tolerate using the `__iterator__` property
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
"laxbreak" : true, // true: Tolerate possibly unsafe line breakings
"laxcomma" : false, // true: Tolerate comma-first style coding
"loopfunc" : false, // true: Tolerate functions being defined in loops
"multistr" : true, // true: Tolerate multi-line strings
"proto" : false, // true: Tolerate using the `__proto__` property
"scripturl" : false, // true: Tolerate script-targeted URLs
"smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment
"shadow" : true, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
"validthis" : false, // true: Tolerate using this in a non-constructor function
// Environments
"browser" : true, // Web Browser (window, document, etc)
"couch" : false, // CouchDB
"devel" : true, // Development/debugging (alert, confirm, etc)
"dojo" : false, // Dojo Toolkit
"jquery" : false, // jQuery
"mootools" : false, // MooTools
"node" : true, // Node.js
"nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
"prototypejs" : false, // Prototype and Scriptaculous
"rhino" : false, // Rhino
"worker" : false, // Web Workers
"wsh" : false, // Windows Scripting Host
"yui" : false, // Yahoo User Interface
"noyield" : true, // allow generators without a yield
// Legacy
"nomen" : false, // true: Prohibit dangling `_` in variables
"onevar" : false, // true: Allow only one `var` statement per function
"passfail" : false, // true: Stop on first error
"white" : false, // true: Check against strict whitespace and indentation rules
// Custom Globals
"globals" : { // additional predefined global variables
// mocha
"describe": true,
"it": true,
"before": true,
"afterEach": true,
"beforeEach": true,
"after": true
}
}

View File

@@ -0,0 +1,8 @@
test/
Makefile
.travis.yml
logo.png
.jshintignore
.jshintrc
.gitingore
coverage/

View File

@@ -0,0 +1,6 @@
language: node_js
node_js:
- '0.11'
- '0.10'
script: "make test-travis"
after_script: "npm install coveralls@2 && cat ./coverage/lcov.info | coveralls"

0
test/fixtures/scope-package/AUTHORS vendored Normal file
View File

21
test/fixtures/scope-package/LICENSE.txt vendored Normal file
View File

@@ -0,0 +1,21 @@
This software is licensed under the MIT License.
Copyright (c) 2014 fengmk2 <fengmk2@gmail.com> and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

51
test/fixtures/scope-package/Makefile vendored Normal file
View File

@@ -0,0 +1,51 @@
TESTS = test/*.test.js
REPORTER = spec
TIMEOUT = 1000
MOCHA_OPTS =
install:
@npm install --registry=https://registry.npm.taobao.org --disturl=https://npm.taobao.org/dist
jshint: install
@./node_modules/.bin/jshint .
test: install
@NODE_ENV=test ./node_modules/.bin/mocha \
--harmony \
--reporter $(REPORTER) \
--timeout $(TIMEOUT) \
$(MOCHA_OPTS) \
$(TESTS)
test-cov cov: install
@NODE_ENV=test node --harmony \
node_modules/.bin/istanbul cover --preserve-comments \
./node_modules/.bin/_mocha \
-- \
--reporter $(REPORTER) \
--timeout $(TIMEOUT) \
$(MOCHA_OPTS) \
$(TESTS)
@./node_modules/.bin/cov coverage
test-travis: install
@NODE_ENV=test node --harmony \
node_modules/.bin/istanbul cover --preserve-comments \
./node_modules/.bin/_mocha \
--report lcovonly \
-- \
--reporter dot \
--timeout $(TIMEOUT) \
$(MOCHA_OPTS) \
$(TESTS)
test-all: install jshint test cov
autod: install
@./node_modules/.bin/autod -w --prefix "~"
@$(MAKE) install
contributors: install
@./node_modules/.bin/contributors -f plain -o AUTHORS
.PHONY: test

64
test/fixtures/scope-package/README.md vendored Normal file
View File

@@ -0,0 +1,64 @@
scope-package
=======
[![NPM version][npm-image]][npm-url]
[![build status][travis-image]][travis-url]
[![Test coverage][coveralls-image]][coveralls-url]
[![Gittip][gittip-image]][gittip-url]
[![David deps][david-image]][david-url]
[npm-image]: https://img.shields.io/npm/v/scope-package.svg?style=flat
[npm-url]: https://npmjs.org/package/scope-package
[travis-image]: https://img.shields.io/travis/node-modules/scope-package.svg?style=flat
[travis-url]: https://travis-ci.org/node-modules/scope-package
[coveralls-image]: https://img.shields.io/coveralls/node-modules/scope-package.svg?style=flat
[coveralls-url]: https://coveralls.io/r/node-modules/scope-package?branch=master
[gittip-image]: https://img.shields.io/gittip/fengmk2.svg?style=flat
[gittip-url]: https://www.gittip.com/fengmk2/
[david-image]: https://img.shields.io/david/node-modules/scope-package.svg?style=flat
[david-url]: https://david-dm.org/node-modules/scope-package
![logo](https://raw.github.com/node-modules/scope-package/master/logo.png)
scope-package desc
## Install
```bash
$ npm install scope-package
```
## Usage
```js
var scope-package = require('scope-package');
scope-package.foo(function (err) {
});
```
## License
(The MIT License)
Copyright (c) 2014 fengmk2 &lt;fengmk2@gmail.com&gt; and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

1
test/fixtures/scope-package/index.js vendored Normal file
View File

@@ -0,0 +1 @@
module.exports = require('./lib/scope-package');

View File

@@ -0,0 +1,17 @@
/**!
* scope-package - lib/scope-package.js
*
* Copyright(c) 2014 fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
"use strict";
/**
* Module dependencies.
*/
var koa = require('koa');

View File

@@ -0,0 +1,44 @@
{
"name": "@cnpm/scope-package",
"version": "0.0.1",
"description": "scope-package",
"main": "index.js",
"scripts": {
"test": "make test-all"
},
"config": {
"cov": {
"threshold": 100
}
},
"dependencies": {
},
"devDependencies": {
"autod": "*",
"contributors": "*",
"should": "*",
"jshint": "*",
"cov": "*",
"istanbul-harmony": "*",
"mocha": "*"
},
"homepage": "https://github.com/node-modules/scope-package",
"repository": {
"type": "git",
"url": "git://github.com/node-modules/scope-package.git",
"web": "https://github.com/node-modules/scope-package"
},
"bugs": {
"url": "https://github.com/node-modules/scope-package/issues",
"email": "fengmk2@gmail.com"
},
"keywords": [
"scope-package"
],
"engines": {
"node": ">= 0.10.0"
},
"author": "fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)",
"license": "MIT"
}

View File

@@ -17,10 +17,21 @@
var crypto = require('crypto');
var utility = require('utility');
var User = require('../proxy/user');
var config = require('../config');
if (process.env.CNPM_SOURCE_NPM) {
config.sourceNpmRegistry = process.env.CNPM_SOURCE_NPM;
}
if (process.env.CNPM_SOURCE_NPM_ISCNPM === 'false') {
config.sourceNpmRegistryIsCNpm = false;
}
var usernames = [
'cnpmjstest101',
'cnpmjstest10'
'cnpmjstest102',
'cnpmjstest10', // admin
'cnpmjstestAdmin2', // other admin
'cnpmjstestAdmin3', // other admin
];
usernames.forEach(function (name) {

View File

@@ -20,6 +20,8 @@ var request = require('supertest');
var app = require('../../servers/registry');
var mm = require('mm');
var mysql = require('../../common/mysql');
var config = require('../../config');
var UserService = require('../../services/user');
describe('middleware/auth.test.js', function () {
before(function (done) {
@@ -60,5 +62,29 @@ describe('middleware/auth.test.js', function () {
.set('authorization', 'basic ' + new Buffer('cnpmjstest10:cnpmjstest10').toString('base64'))
.expect(500, done);
});
describe('config.customUserService = true', function () {
beforeEach(function () {
mm(config, 'customUserService', true);
});
it('should 401 when user service auth throw error', function (done) {
mm(UserService, 'auth', function* () {
var err = new Error('mock user service auth error, please visit http://ooxx.net/user to sigup first');
err.name = 'UserSeriveAuthError';
err.status = 401;
throw err;
});
request(app)
.put('/-/user/org.couchdb.user:cnpmjstest10/-rev/1')
.set('authorization', 'basic ' + new Buffer('cnpmjstest10:cnpmjstest10').toString('base64'))
.expect({
error: 'UserSeriveAuthError',
reason: 'mock user service auth error, please visit http://ooxx.net/user to sigup first'
})
.expect(401, done);
});
});
});
});

View File

@@ -16,15 +16,22 @@
var should = require('should');
var request = require('supertest');
var utils = require('../utils');
var app = require('../../servers/web');
var registry = require('../../servers/registry');
describe('middleware/web_not_found.test.js', function () {
before(function (done) {
app.listen(0, done);
});
// make sure mk2testmodule exists
var baseauth = 'Basic ' + new Buffer('cnpmjstest10:cnpmjstest10').toString('base64');
// name: mk2testmodule
var pkg = utils.getPackage('mk2testmodule');
after(function (done) {
app.close(done);
request(registry)
.put('/' + pkg.name)
.set('authorization', utils.adminAuth)
.send(pkg)
.end(done);
});
describe('web_not_found()', function () {

View File

@@ -16,34 +16,40 @@
var should = require('should');
var Dist = require('../../proxy/dist');
var fs = require('fs');
var nfs = require('../../common/nfs');
describe('proxy/dist.test.js', function () {
describe('savefile() and getfile', function () {
it('should save and get /npm-versions.txt', function* () {
var name = 'npm-versions.txt';
var info = {
name: 'npm-versions.txt',
name: name,
parent: '/',
date: '15-Sep-2011 23:48',
size: 1676,
url: 'http://cnpmjs.org/dist/npm-versions.txt',
url: name,
sha1: '104731881047318810473188'
};
yield* Dist.savefile(info);
fs.writeFileSync(nfs._getpath('npm-versions.txt'), 'npm version');
var got = yield* Dist.getfile('/npm-versions.txt');
should.exist(got);
got.should.eql(info);
});
it('should save and get /v1.0.0/npm-versions.txt', function* () {
var name = 'v1.0.0/npm-versions.txt';
var info = {
name: 'npm-versions.txt',
parent: '/v1.0.0/',
date: '15-Sep-2011 23:48',
size: 1676,
url: 'http://cnpmjs.org/dist/v1.0.0/npm-versions.txt',
url: 'v1.0.0/npm-versions.txt',
sha1: '104731881047318810473188'
};
yield* Dist.savefile(info);
fs.writeFileSync(nfs._getpath('npm-versions.txt'), 'npm version 1.0.0');
var got = yield* Dist.getfile('/v1.0.0/npm-versions.txt');
should.exist(got);
got.should.eql(info);

View File

@@ -18,13 +18,30 @@ var should = require('should');
var mm = require('mm');
var fs = require('fs');
var path = require('path');
var request = require('supertest');
var mysql = require('../../common/mysql');
var Module = require('../../proxy/module');
var config = require('../../config');
var utils = require('../utils');
var app = require('../../servers/registry');
var fixtures = path.join(path.dirname(__dirname), 'fixtures');
var id;
var pkgname = 'module-proxy-test-pkg';
var pkgversion = '1.0.0';
describe('proxy/module.test.js', function () {
before(function (done) {
var pkg = utils.getPackage(pkgname, pkgversion, utils.admin);
request(app.listen())
.put('/' + pkgname)
.set('authorization', utils.adminAuth)
.send(pkg)
.expect(201, done);
});
afterEach(mm.restore);
describe('addTag()', function () {
@@ -144,26 +161,48 @@ describe('proxy/module.test.js', function () {
});
describe('listByAuthor()', function () {
it('should return author recent modules', function (done) {
Module.listByAuthor('fengmk2', function (err, rows) {
should.not.exist(err);
rows.forEach(function (r) {
r.should.have.keys('name', 'description');
});
done();
it('should return author recent modules', function* () {
var rows = yield Module.listByAuthor('fengmk2');
rows.forEach(function (r) {
r.should.have.keys('name', 'description');
});
});
});
describe('updateReadme()', function () {
it('should update ok', function (done) {
Module.updateReadme(id, 'test', function (err, data) {
should.not.exist(err);
Module.getById(id, function (err, data) {
should.not.exist(err);
data.package.readme.should.equal('test');
done();
});
it('should update ok', function* () {
var row = yield Module.get(pkgname, pkgversion);
should.exist(row);
yield* Module.updateReadme(row.id, 'test');
var data = yield Module.getById(row.id);
data.package.readme.should.equal('test');
});
});
describe('removeTagsByNames()', function () {
it('should work', function* () {
yield* Module.removeTagsByNames('foo', ['latest', '1.0']);
});
});
describe('listPrivates()', function () {
it('should response [] if scopes not present', function* () {
mm(config, 'scopes', []);
var modules = yield Module.listPrivates();
modules.should.eql([]);
});
it('should response [] if private modules not present', function* () {
mm(config, 'privatePackages', []);
mm(config, 'scopes', ['@not-exist']);
var modules = yield Module.listPrivates();
modules.should.eql([]);
});
it('should work', function* () {
var modules = yield Module.listPrivates();
modules.forEach(function (m) {
m.should.have.keys(['name', 'description']);
});
});
});

View File

@@ -0,0 +1,69 @@
/**!
* cnpmjs.org - test/proxy/module_maintainer.test.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var should = require('should');
var ModuleMaintainer = require('../../proxy/module_maintainer');
describe('proxy/module_maintainer.test.js', function () {
describe('update()', function () {
it('should update one maintainer', function* () {
var rs = yield* ModuleMaintainer.update('testfoo', ['fengmk2']);
rs.should.eql({
add: ['fengmk2'],
remove: []
});
// again should be fine
var rs = yield* ModuleMaintainer.update('testfoo', ['fengmk2']);
rs.should.eql({
add: ['fengmk2'],
remove: []
});
// remove the exists
var rs = yield* ModuleMaintainer.update('testfoo', ['fengmk2-1', 'foobar']);
rs.should.eql({
add: ['fengmk2-1', 'foobar'],
remove: ['fengmk2']
});
});
it('should update multi maintainers', function* () {
var rs = yield* ModuleMaintainer.update('testfoo2', ['fengmk2', 'ok', 'foobar']);
rs.should.eql({
add: ['fengmk2', 'ok', 'foobar'],
remove: []
});
// remove exists
var rs = yield* ModuleMaintainer.update('testfoo2', ['fengmk2']);
rs.should.eql({
add: ['fengmk2'],
remove: ['ok', 'foobar']
});
var rs = yield* ModuleMaintainer.update('testfoo3', ['fengmk2', 'ok', 'foobar']);
rs.should.eql({
add: ['fengmk2', 'ok', 'foobar'],
remove: []
});
});
it('should add empty maintainers do nothing', function* () {
var rs = yield* ModuleMaintainer.update('tesfoobar', []);
rs.should.eql({
add: [],
remove: []
});
});
});
});

View File

@@ -26,17 +26,17 @@ var fixtures = path.join(path.dirname(__dirname), 'fixtures');
describe('proxy/npm.test.js', function () {
afterEach(mm.restore);
it('should return a module info from source npm', function *() {
it('should return a module info from source npm', function* () {
var data = yield npm.get('pedding');
data.name.should.equal('pedding');
});
it('should return null when module not exist', function *() {
it('should return null when module not exist', function* () {
var data = yield npm.get('pedding-not-exists');
should.not.exist(data);
});
it('should return error when http error', function *() {
it.skip('should return error when http error', function* () {
mm.http.request(/\//, new ChunkStream(['{']));
try {
var data = yield npm.get('pedding-not-exists');
@@ -46,7 +46,7 @@ describe('proxy/npm.test.js', function () {
}
});
it('should return ServerError when http 500 response', function *() {
it('should return ServerError when http 500 response', function* () {
var content = fs.createReadStream(path.join(fixtures, '500.txt'));
mm.http.request(/\//, content, { statusCode: 500 });
// http://registry.npmjs.org/octopie
@@ -58,4 +58,12 @@ describe('proxy/npm.test.js', function () {
err.message.should.equal('Status 500, ' + fs.readFileSync(path.join(fixtures, '500.txt'), 'utf8'));
}
});
describe('getPopular()', function () {
it('should return popular modules', function* () {
var names = yield npm.getPopular(10);
names.should.have.a.lengthOf(10);
names[0].should.equal('underscore');
});
});
});

View File

@@ -15,11 +15,15 @@
*/
var should = require('should');
var mm = require('mm');
var SyncModuleWorker = require('../../proxy/sync_module_worker');
var mysql = require('../../common/mysql');
var Log = require('../../proxy/module_log');
var config = require('../../config');
describe('proxy/sync_module_worker.test.js', function () {
afterEach(mm.restore);
it('should start a sync worker', function (done) {
Log.create({
name: 'mk2testmodule',
@@ -38,6 +42,25 @@ describe('proxy/sync_module_worker.test.js', function () {
});
});
it('should sync upstream first', function (done) {
mm(config, 'sourceNpmRegistryIsCNpm', true);
Log.create({
name: 'mk2testmodule',
username: 'fengmk2',
}, function (err, result) {
should.not.exist(err);
result.id.should.above(0);
var worker = new SyncModuleWorker({
logId: result.id,
name: 'mk2testmodule',
username: 'fengmk2'
});
worker.start();
worker.on('end', done);
});
});
it('should start a sync worker with names and noDep', function (done) {
var worker = new SyncModuleWorker({
name: ['mk2testmodule'],
@@ -63,4 +86,42 @@ describe('proxy/sync_module_worker.test.js', function () {
worker.start();
worker.on('end', done);
});
it('should sync unpublished module by name', function* () {
var result = yield* SyncModuleWorker.sync('tnpm', 'fengmk2');
result.should.be.Number;
});
it('should sync not exists module', function* () {
var result = yield* SyncModuleWorker.sync('tnpm-not-exists', 'fengmk2');
result.should.be.Number;
});
it('should sync unpublished info', function (done) {
var worker = new SyncModuleWorker({
name: ['tnpm'],
username: 'fengmk2'
});
worker.start();
worker.on('end', function () {
var names = worker.successes.concat(worker.fails);
names.sort();
names.should.eql(['tnpm']);
done();
});
});
describe('syncUpstream()', function () {
it('should sync upstream work', function* () {
var worker = new SyncModuleWorker({
name: ['tnpm'],
username: 'fengmk2'
});
yield [
worker.syncUpstream('tnpm'),
worker.syncUpstream('pedding'),
];
});
});
});

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