Compare commits

...

520 Commits

Author SHA1 Message Date
fengmk2
8e2f0052dc Release 2.0.0-beta0 2014-11-02 23:50:41 +08:00
Yiyu He
6e77a171fd Merge pull request #488 from cnpm/limit-log-size
fix(module_log): limit module sync log size to 1MB
2014-11-02 11:48:03 +08:00
fengmk2
3b08a7fd21 Merge pull request #490 from cnpm/issue482-remarkable
ungrade koa-markdown to use remarkable, close #482
2014-11-02 11:42:01 +08:00
dead_horse
e3f4d8d158 ungrade koa-markdown to use remarkable, close #482 2014-11-02 02:55:14 +08:00
fengmk2
5b3d92ee43 fix(module_log): limit module sync log size to 1MB
If log size bigger than 1MB, it will be auto slice to 0.5MB
2014-11-01 22:32:46 +08:00
fengmk2
c1e35cabe3 refactor(config): remove adaptScope config key 2014-11-01 22:03:39 +08:00
fengmk2
63d604d9bb chore(Makefile): $ make install-production 2014-10-31 16:09:00 +08:00
fengmk2
b402f04fc9 fix(sequelize): show warnning message when using old config.js 2014-10-31 16:05:07 +08:00
fengmk2
a4920fdcee docs(readme): Migrating from 1.x to 2.x
Add new wiki: https://github.com/cnpm/cnpmjs.org/wiki/Migrating-from-1.x-to-2.x
2014-10-31 15:22:22 +08:00
Yiyu He
798f357132 Merge pull request #484 from cnpm/sequelize-merge
Sequelize instead of raw SQL
2014-10-31 14:31:55 +08:00
fengmk2
9ce8482a13 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:16:16 +08:00
fengmk2
23067fa1a2 refactor(dispatch): remove unused codes 2014-10-31 14:11:44 +08:00
fengmk2
abdbf69f25 remove duplicate db_engine 2014-10-30 16:01:59 +08:00
fengmk2
a4d7331f52 fix blank line 2014-10-30 15:42:42 +08:00
fengmk2
fac45324b9 fix(sync): make sure save log in sequeue 2014-10-30 15:36:58 +08:00
fengmk2
98c750df1a show db engine on registry:/ 2014-10-30 14:30:57 +08:00
fengmk2
22335db1de docs(readme): change dev on local database deps 2014-10-30 14:15:35 +08:00
fengmk2
47ad267df5 fix tests 2014-10-30 13:42:51 +08:00
fengmk2
49b27d562d merge sequelize 2014-10-30 13:38:01 +08:00
fengmk2
f49ce17457 refactor(sync): fix sync/*.js tests 2014-10-30 12:11:15 +08:00
fengmk2
128383d032 refactor(controllers): controllers all change to ORM 2014-10-29 23:43:00 +08:00
fengmk2
f47615501c web/package/*.js done 2014-10-29 18:11:40 +08:00
fengmk2
adae52cef3 /package/:name refactor done 2014-10-29 17:35:56 +08:00
fengmk2
30407dee07 merge download.js to total.js 2014-10-29 16:35:10 +08:00
fengmk2
1ba6f08f4b rename download filename 2014-10-29 11:25:58 +08:00
fengmk2
083104e2e1 registry controller done 2014-10-29 11:19:58 +08:00
fengmk2
12b09ab559 fix use_package.js 2014-10-29 02:04:39 +08:00
fengmk2
cf910e1d8c add list package names since startkey 2014-10-29 00:14:41 +08:00
fengmk2
eda52b0a3e list all package names 2014-10-28 19:25:36 +08:00
fengmk2
cd2fd4a0b5 fix test 2014-10-28 19:15:03 +08:00
fengmk2
8cfa7a4d8b improve coverage 2014-10-28 18:56:36 +08:00
fengmk2
716efab4ea add remove package test cases 2014-10-28 18:44:50 +08:00
fengmk2
a0ec1c8521 refactor remove package 2014-10-28 14:59:34 +08:00
fengmk2
be1a966ce1 add package publish and add tag test cases 2014-10-28 14:14:20 +08:00
fengmk2
c5b5064e40 module controllers 拆分到 controllers/package/*.js 2014-10-28 00:14:27 +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
fengmk2
dd092b356d user data all from services/user.js 2014-10-20 01:56:47 +08:00
fengmk2
d3e93f3990 remove all proxy/*.js 2014-10-20 00:54:33 +08:00
fengmk2
07accd62e4 mv proxy/module_unpublished to models/module_unpublished 2014-10-20 00:31:46 +08:00
fengmk2
8c280bd789 refactor registry download request 2014-10-19 23:49:41 +08:00
fengmk2
3d0674fea3 mv proxy/module_maintainer to models/module_maintainer 2014-10-19 23:29:53 +08:00
fengmk2
393bd42938 mv proxy/dist.js to services/dist.js 2014-10-19 01:46:25 +08:00
fengmk2
1ac1cf82d8 mv proxy/module_log to services/module_log 2014-10-19 00:58:17 +08:00
fengmk2
206d595f02 refator module dependency 2014-10-19 00:36:29 +08:00
fengmk2
aa135d2a30 mv proxy/module.js into services/package.js 2014-10-16 00:01:55 +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
fengmk2
d30260f301 --build-from-source 2014-10-13 03:29:49 +08:00
fengmk2
c8990083a7 mv proxy/total.js to services/total.js 2014-10-13 03:17:44 +08:00
fengmk2
ef246c2ec4 controllers/total.js done 2014-10-13 02:10:38 +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
48d8cb81fc init database before run test 2014-10-12 22:09:19 +08:00
fengmk2
dd9f4a7f2e change proxy/user.js => models/user.js 2014-10-12 21:29:32 +08:00
fengmk2
56eb1b1640 use sequelize to connect database 2014-10-12 14:07:22 +08:00
fengmk2
4b7a191b73 Change version to 2.0.0 2014-10-12 13:03: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
fengmk2
1f219cbd1b Release 0.5.1 2014-05-28 09:18:15 +08:00
dead_horse
17025d42b4 Merge pull request #344 from cnpm/list-since-from-tag
fix attack on /-/all/since?stale=update_after&startkey=2 close #336
2014-05-28 09:09:43 +08:00
fengmk2
922f26b052 fix attack on /-/all/since?stale=update_after&startkey=2 close #336
Also add modified_time key to tag
2014-05-28 00:19:26 +08:00
dead_horse
9371685188 bump thunkify-wrap 2014-05-26 21:44:51 +08:00
dead_horse
ad0b66c7e5 bump koa-middlewares 2014-05-25 01:03:19 +08:00
dead_horse
5588880ec0 remove outputError 2014-05-25 00:44:26 +08:00
dead_horse
9ad72cbdcb bump dependencies 2014-05-25 00:43:16 +08:00
dead_horse
4d033471fc use svg badge 2014-05-25 00:36:58 +08:00
fengmk2
590b7f7eee Merge pull request #340 from cnpm/404-page
add package/notfound page
2014-05-19 19:26:34 +08:00
dead_horse
0ce817f9f3 add package/notfound page 2014-05-19 19:16:15 +08:00
fengmk2
59af2bfd56 add dist mirror link to home page 2014-05-13 09:37:46 +08:00
fengmk2
a83f6bb183 fix sync listdiff and add more test cases 2014-05-13 09:28:54 +08:00
fengmk2
b6f1531743 Release 0.5.0 2014-05-13 08:32:51 +08:00
dead_horse
42d0b538ca Merge pull request #337 from cnpm/dist-sync
Dist sync
2014-05-12 23:48:52 +08:00
fengmk2
a240eb9922 filter /nightlies/* 2014-05-12 17:32:06 +08:00
fengmk2
6853b73fb2 use koa setter instead of set() 2014-05-12 17:09:51 +08:00
fengmk2
a887acec05 update co-urllib 2014-05-12 10:19:19 +08:00
fengmk2
1d3f0f0ad3 add more info on error email 2014-05-12 10:03:01 +08:00
fengmk2
20e04065df add sync dist to sync/index.js 2014-05-12 07:38:48 +08:00
fengmk2
1cee298bc3 show dist page 2014-05-11 20:56:40 +08:00
fengmk2
854bc6c9d8 sync dist file and save it to database 2014-05-10 18:59:13 +08:00
fengmk2
b70c1c421a disable gzip before #335 has fix 2014-05-04 20:05:55 +08:00
dead_horse
b1b6172892 Release 0.4.3 2014-04-18 16:15:50 +08:00
fengmk2
e5a77a4368 Merge pull request #334 from cnpm/fix-permission
add permission check to /:name/:tag
2014-04-18 14:34:27 +08:00
dead_horse
b74cccd342 add permission check to /:name/:tag 2014-04-18 14:29:42 +08:00
fengmk2
6aae538f49 Merge pull request #333 from cnpm/issue332-tag
add put /:name/:tag, close #332
2014-04-18 14:12:56 +08:00
dead_horse
74c8b25374 fix space 2014-04-18 14:07:49 +08:00
dead_horse
0cec2edea6 add put /:name/:tag, close #332 2014-04-18 12:18:07 +08:00
fengmk2
b59a0a99cf Release 0.4.2 2014-04-17 17:33:17 +08:00
dead_horse
c7e82809e3 Merge pull request #330 from cnpm/package-size-show
fix fav ico and show pkg size on pkg info page. fix #318
2014-04-17 17:31:06 +08:00
fengmk2
206ee505a8 sync interval config 2014-04-17 17:28:27 +08:00
fengmk2
d39838f930 fix fav ico and show pkg size on pkg info page. fix #318 2014-04-17 14:59:38 +08:00
dead_horse
512e21aaf5 Merge pull request #329 from cnpm/fix-stack-over
sync work sync one done must wait for a defer.setImmediate. fix #328
2014-04-17 14:12:22 +08:00
fengmk2
f7904ee699 sync work sync one done must wait for a defer.setImmediate. fix #328 2014-04-17 09:11:46 +08:00
dead_horse
7007be6ef4 bump dep versions 2014-04-16 15:17:14 +08:00
dead_horse
70aa3299e5 Merge pull request #326 from cnpm/noattachment
if download tarball 404, throw err better than ignore it. fixed #325
2014-04-16 12:00:29 +08:00
fengmk2
a04f5d68eb if download tarball 404, throw err better than ignore it. fixed #325 2014-04-16 11:44:28 +08:00
fengmk2
7cfe3b58ce refator sync 2014-04-16 11:19:32 +08:00
fengmk2
afdf0bc653 Merge pull request #323 from cnpm/hot-fix-status
hotfix, close #319
2014-04-16 09:39:15 +08:00
fengmk2
42055ac91e Merge pull request #324 from cnpm/hotfix-dist
hotfix, close #321
2014-04-16 09:31:48 +08:00
dead_horse
b93e0dab41 hotfix, close #321 2014-04-16 09:28:22 +08:00
dead_horse
01f2187830 hotfix, close #319 2014-04-16 09:23:59 +08:00
fengmk2
c2f49fcdd9 support custom web home page 2014-04-14 15:14:28 +08:00
dead_horse
3925ef6044 Merge pull request #316 from cnpm/first-full-sync
npm get short only can read from cnpm now
2014-04-14 14:04:59 +08:00
fengmk2
85494d2ba0 npm get short only can read from cnpm now 2014-04-14 13:57:04 +08:00
dead_horse
660ca394d6 Merge pull request #315 from cnpm/local-bindding
Local bindding and redis fix
2014-04-14 10:47:39 +08:00
fengmk2
ad7eeac00c if using reverted proxy like nginx, only binding on local host 2014-04-14 10:43:49 +08:00
fengmk2
f63b72891b fix redis detect logic 2014-04-14 10:27:46 +08:00
fengmk2
28d31b093d Release 0.4.1 2014-04-10 14:46:01 +08:00
fengmk2
466e14e92a Merge pull request #313 from cnpm/hotfix-sync-status
fix sync status code error
2014-04-10 07:46:57 +08:00
dead_horse
3646c2ce50 fix sync status code error 2014-04-10 02:39:02 +08:00
fengmk2
fa44097d8f Release 0.4.0 2014-04-09 21:05:37 +08:00
dead_horse
0c56294c47 Merge pull request #312 from cnpm/local-mysql-dev-env
Local mysql dev env
2014-04-09 20:53:12 +08:00
fengmk2
41552fd27c fix test cases to run on local machine 2014-04-09 19:37:37 +08:00
fengmk2
cb4d4f51dd add contribute guidelines 2014-04-09 09:58:23 +08:00
fengmk2
1b266d527c use local mysql for dev env. fix #308 2014-04-09 09:31:34 +08:00
fengmk2
9d660be259 Merge pull request #306 from cnpm/use-copy-to
use copy to
2014-04-01 17:37:36 +08:00
dead_horse
b041cc7361 use copy to 2014-04-01 16:19:49 +08:00
dead_horse
e420f6985e Merge pull request #305 from cnpm/fix-test
fix test case
2014-03-31 17:51:15 +08:00
fengmk2
60dc5cf5fa fix test case 2014-03-31 17:47:10 +08:00
fengmk2
54916f49e5 Merge pull request #303 from cnpm/compress-conditional
use koa-compress and koa-conditional-get
2014-03-31 14:52:39 +08:00
dead_horse
a3cafaa297 use koa-compress and koa-conditional-get 2014-03-31 14:21:13 +08:00
fengmk2
aa13a100d4 Merge pull request #302 from cnpm/typeerror-hotfix
maintainers is string, fix #301
2014-03-30 09:01:23 +08:00
fengmk2
3b29310826 maintainers is string, fix #301 2014-03-30 09:01:03 +08:00
fengmk2
6d76a590e5 Release 0.3.13 2014-03-27 16:28:01 +08:00
fengmk2
3a98b63736 Merge pull request #299 from cnpm/fix-user-update
fix npm adduser update 409 bug
2014-03-27 16:24:34 +08:00
fengmk2
f5a2090fda Merge pull request #298 from cnpm/fix-ml-cov
fix multiline coverage
2014-03-27 16:18:23 +08:00
fengmk2
989a69143f fix npm adduser update 409 bug 2014-03-27 16:18:01 +08:00
dead_horse
77e6db4f0e fix multiline coverage 2014-03-27 16:03:00 +08:00
fengmk2
a3f7affe7b Release 0.3.13 2014-03-27 15:47:19 +08:00
dead_horse
2cd0453c6b Merge pull request #297 from cnpm/pkg-engine
show package engines. fixed #280
2014-03-27 15:23:32 +08:00
fengmk2
84bc126d57 show package engines. fixed #280 2014-03-27 15:13:18 +08:00
dead_horse
760421661c Merge pull request #296 from cnpm/sync-with-local
dont sync local package field. fix #295
2014-03-27 15:04:25 +08:00
fengmk2
425e430a3a dont sync local package field. fix #295 2014-03-27 14:55:52 +08:00
fengmk2
ad3602bf15 Release 0.3.12 2014-03-26 15:28:05 +08:00
fengmk2
2c5852b344 Merge pull request #294 from cnpm/hotfix-sync
fix result.successes not exist error
2014-03-26 15:22:59 +08:00
dead_horse
115a349fc5 fix result.successes not exist error 2014-03-26 15:21:37 +08:00
fengmk2
3ceea3ccf4 Merge pull request #293 from cnpm/search-list
fix search list
2014-03-24 14:20:51 +08:00
dead_horse
ab6a9b2bea fix search list 2014-03-24 14:17:22 +08:00
fengmk2
9cc84abbb5 Merge pull request #292 from cnpm/issue291-all
only return package name in /-/all and /-/all/since, fixed #291
2014-03-24 11:27:31 +08:00
dead_horse
c1c07cf2e4 add simple request for listall 2014-03-24 11:11:54 +08:00
dead_horse
ce5c97956b only return package name in /-/all and /-/all/since, fixed #291 2014-03-24 10:31:28 +08:00
dead_horse
7cb7a517da refine docs foloder 2014-03-23 02:28:03 +08:00
dead_horse
c7df6f0f1a fix typo 2014-03-23 02:26:38 +08:00
fengmk2
ee59176b67 Merge pull request #290 from cnpm/modified-as-etag
use module gmt_modified as etag. fix #288
2014-03-22 02:09:42 +08:00
fengmk2
f160d741cb use module gmt_modified as etag. fix #288 2014-03-22 01:55:13 +08:00
dead_horse
94974f81ae fix typo, remove unused config in package.json 2014-03-21 17:26:54 +08:00
fengmk2
41c0fea3dd Merge pull request #289 from cnpm/readme
Readme
2014-03-21 16:58:27 +08:00
dead_horse
dfae519e96 fix typo 2014-03-21 16:56:15 +08:00
dead_horse
4d7d4f06b1 web page only list cnpm registry related info 2014-03-21 16:25:47 +08:00
dead_horse
5a890ff27e update readme 2014-03-21 16:15:38 +08:00
fengmk2
ac124d0e2d Merge pull request #286 from cnpm/qnfs-generator
use generator in qnfs
2014-03-20 21:17:03 +08:00
dead_horse
7e267156eb use generator in qnfs 2014-03-20 20:35:58 +08:00
fengmk2
258c34fea8 Release 0.3.11 2014-03-20 10:41:37 +08:00
fengmk2
cd9d403237 Merge pull request #284 from cnpm/issue283-maintainer
use common.isMaintainer, fixed #283
2014-03-20 10:35:55 +08:00
dead_horse
6d43612044 use common.isMaintainer, fixed #283 2014-03-20 09:49:47 +08:00
fengmk2
2e99a779c7 Merge pull request #282 from cnpm/update-thunkify
update thunkify-wrap, breaking change in thunkify-wrap
2014-03-18 10:49:39 +08:00
dead_horse
43259ed126 update dependencies 2014-03-18 10:44:21 +08:00
fengmk2
51e32e3ece Merge pull request #281 from cnpm/issue279-co-mocha
use co-mocha for test, fixed #279
2014-03-18 09:35:39 +08:00
dead_horse
37d9d62d3f use co-mocha for test, fixed #279 2014-03-17 22:27:43 +08:00
dead_horse
2e0a1ca3e8 update thunkify-wrap, breaking change in thunkify-wrap 2014-03-17 17:36:37 +08:00
fengmk2
5abaee4f2a Merge pull request #278 from cnpm/use-multiline
Use multiline
2014-03-17 14:06:59 +08:00
dead_horse
5e2ca1d5e8 refactor SQLs by using multiline 2014-03-17 13:27:09 +08:00
dead_horse
c0a6bb4869 fix typo 2014-03-16 22:28:56 +08:00
dead_horse
2aba0836cf use multiline to refactor sqls 2014-03-16 22:20:11 +08:00
fengmk2
e1636e2c7d ignore contributors 2014-03-16 16:43:58 +08:00
fengmk2
15245dc68b Release 0.3.10 2014-03-16 16:33:02 +08:00
dead_horse
8dedabaa2d Merge pull request #277 from cnpm/only-authsession-send-cookie
Only /_session request send the authSession. fixed #223
2014-03-16 16:24:35 +08:00
fengmk2
f4b82bbf0b Only /_session request send the authSession. fixed #223
* no auth requests url dont send the authSession
* baseauth dont create session cookie too
2014-03-16 16:14:12 +08:00
dead_horse
78d81c02cd Merge pull request #276 from cnpm/sync-npm-user
Sync npm user
2014-03-16 15:28:31 +08:00
fengmk2
1c743f3759 sync npm user info when maintainers and contributors not exists. fixed #82 2014-03-16 14:58:44 +08:00
fengmk2
041f80ab14 save npm user to mysql 2014-03-16 12:44:38 +08:00
fengmk2
e5646c6553 password salt always be randoms 2014-03-16 11:51:06 +08:00
fengmk2
6134983bcc Merge pull request #275 from cnpm/issue274-sync-by-install
remove session access in /name and /name/version, fixed #274
2014-03-16 11:36:27 +08:00
dead_horse
7dab569d43 remove session access in /name and /name/version, fixed #274 2014-03-15 23:20:34 +08:00
fengmk2
5aa84b03c6 Merge pull request #270 from cnpm/update-session
Update session
2014-03-15 22:59:23 +08:00
dead_horse
77fc133ecb fix update maintainer session error 2014-03-15 22:56:03 +08:00
dead_horse
f98416e217 update koa-middlewares 2014-03-15 22:52:50 +08:00
dead_horse
5f49b97aa4 fix test, fix sync_by_install 2014-03-15 22:52:50 +08:00
dead_horse
8bdfd593c9 use defer session 2014-03-15 22:52:48 +08:00
dead_horse
c214da39dc Merge pull request #272 from cnpm/author-add
Support npm owner|author add [name] [pkg]. fixed #271
2014-03-15 22:41:34 +08:00
fengmk2
f2be453ef6 Support npm owner|author add [name] [pkg]. fixed #271
* npm 1.4.4+ adduser new flow support
* npm author add, rm, ls support
2014-03-15 17:15:42 +08:00
fengmk2
e2b7003749 Release 0.3.9 2014-03-14 15:05:45 +08:00
fengmk2
944ac6a712 custom user-agent 2014-03-14 15:04:29 +08:00
dead_horse
c502c00063 Merge pull request #269 from cnpm/co-urllib
use co-urllib instead of thunkify urllib; fix mock http.request test cases
2014-03-14 14:42:12 +08:00
fengmk2
d536ad954b use co-urllib instead of thunkify urllib; fix mock http.request test cases 2014-03-14 14:33:20 +08:00
fengmk2
100706f311 request limit custom message 2014-03-13 10:47:06 +08:00
fengmk2
8d8fd31a0e Merge pull request #268 from cnpm/issue267-limit
add koa-limit, fixed #267
2014-03-13 09:55:30 +08:00
dead_horse
7aa2ea3086 add config.redis check 2014-03-13 09:31:30 +08:00
dead_horse
5bf40164a1 add koa-limit, fixed #267 2014-03-13 01:34:36 +08:00
fengmk2
e497fb354d Release 0.3.8 2014-03-11 15:13:11 +08:00
fengmk2
e647d4a1fe Merge pull request #265 from cnpm/update-middlewares
update middlewares, fixed 264
2014-03-11 15:08:33 +08:00
dead_horse
33465ecf86 update middlewares, fixed 264 2014-03-11 15:06:56 +08:00
fengmk2
3eb3eb6be9 Release 0.3.7 2014-03-11 12:02:39 +08:00
fengmk2
6ab1556ec0 show worker die date time 2014-03-10 13:50:38 +08:00
fengmk2
9fd21a5c25 Merge pull request #261 from cnpm/update-koa
update to koa@0.5.1
2014-03-08 10:35:20 +08:00
dead_horse
53b8398e93 update to koa@0.5.1 2014-03-08 10:30:27 +08:00
fengmk2
c43b117e67 hotfix for star user 2014-03-08 00:23:11 +08:00
fengmk2
7db269e5f4 Merge pull request #253 from cnpm/issue237-sync-worker
Issue237 sync worker
2014-03-07 23:48:09 +08:00
dead_horse
92ed8984d3 fix yield gather, sync missing deps even no missing versions 2014-03-07 23:45:13 +08:00
dead_horse
71d351da29 fix return versions 2014-03-07 22:36:13 +08:00
dead_horse
c8e1d8cc4a fix makefile, remove eventproxy 2014-03-07 22:36:13 +08:00
dead_horse
fd79879d0c refactor sync_module_worker 2014-03-07 22:36:13 +08:00
dead_horse
354b82f6ea refactor sync_module_worker 2014-03-07 22:36:13 +08:00
dead_horse
de0487cccc add make test-dev, fixed #259 2014-03-07 22:35:15 +08:00
dead_horse
081ccd66df change npm.js to generator 2014-03-07 22:35:15 +08:00
dead_horse
cebf501182 update urllib, proxy/npm.js use generator
but SyncModuleWorker still use callback type npm.js,
so just use co wrap to thunk.
after refactor SyncModuleWorker, remove this co wrap.
2014-03-07 22:35:14 +08:00
dead_horse
8fe593a182 sync_all and sync_exist to generator 2014-03-07 22:34:43 +08:00
dead_horse
8770458a46 change function to generator 2014-03-07 22:34:43 +08:00
dead_horse
559dc03cd4 need node >= v0.11.9 2014-03-06 16:16:36 +08:00
fengmk2
70d12f235a Release 0.3.6 2014-03-06 15:05:07 +08:00
fengmk2
aa7077e4b0 install missing package should sync it from source npm. fixed #252 2014-03-06 15:04:07 +08:00
dead_horse
f5c19668f5 Merge pull request #254 from cnpm/jshint
Add jshint check: $ make jshint
2014-03-06 14:09:07 +08:00
fengmk2
2b2a1904c6 npm publish dont contains .jshint* 2014-03-06 14:02:33 +08:00
fengmk2
a234c7e289 npm test run jshint 2014-03-06 14:00:43 +08:00
fengmk2
63562c30ec Add jshint check: $ make jshint
* .jshint make esnext true
* .jshintignore ignore no need check dirs
2014-03-06 13:55:13 +08:00
dead_horse
6968331b38 Merge pull request #251 from cnpm/yield-next
use `yield* next` instead of `yield next`
2014-03-05 16:55:32 +08:00
fengmk2
fc1a17e4d1 use yield* next instead of yield next
* According to koajs/compose#2, show performances improve a lot.
2014-03-05 16:49:58 +08:00
fengmk2
3872b7dfdf replace dist.u.qiniudn.com with cnpmjs.org/dist 2014-03-05 13:20:45 +08:00
fengmk2
e340ce081a Release 0.3.5 2014-03-05 13:18:39 +08:00
dead_horse
170a689168 Merge pull request #250 from cnpm/dist
redirect /dist/xxx.tgz => http://dist.u.qiniudn.com/xxx.tgz fixed #249
2014-03-05 11:08:05 +08:00
fengmk2
7794d1099d redirect /dist/xxx.tgz => http://dist.u.qiniudn.com/xxx.tgz fixed #249 2014-03-05 11:00:41 +08:00
dead_horse
bd8686c448 Merge pull request #247 from cnpm/redirect
redirect /name to /package/name when /name is 404. fixed #245
2014-03-04 23:54:48 +08:00
fengmk2
d8a76d03e0 redirect /name to /package/name when /name is 404. fixed #245 2014-03-04 23:22:32 +08:00
dead_horse
4460810799 Merge pull request #244 from cnpm/missing-propertys
Add missing properies and sync missing star users. fixed #235
2014-03-04 21:35:57 +08:00
fengmk2
4124525eb8 Add missing properies and sync missing star users. fixed #235
* add readmeFilename and missing created, modified time.
* Sync star users on pkg sync flow
* Show star users on registry.show
* Show star users on web package show
* Need to create new table module_star
2014-03-04 21:20:43 +08:00
fengmk2
ff50946cd3 Release 0.3.4 2014-03-04 16:32:25 +08:00
fengmk2
3637eeefdd add cov 2014-03-04 16:31:22 +08:00
dead_horse
cecb41e6ea Merge pull request #243 from cnpm/coverage
use istanbul run test coverage
2014-03-03 21:52:52 +08:00
fengmk2
72b3240ef5 use istanbul run test coverage 2014-03-03 21:02:11 +08:00
dead_horse
869d5681ce Merge pull request #242 from cnpm/gzip
gzip support. fix #241
2014-03-03 16:45:37 +08:00
fengmk2
ff1bfd5acc gzip support. fix #241 2014-03-03 16:44:29 +08:00
dead_horse
7e3129c8b8 Merge pull request #240 from stanzheng/patch-1
readme spelling patch
2014-03-03 00:42:27 +08:00
Stanley Zheng
0618c732d7 thanks dead_horse 2014-03-02 11:39:30 -05:00
Stanley Zheng
9b34ab408c readme spelling patch 2014-03-02 11:10:40 -05:00
fengmk2
d990d3aa91 Merge pull request #234 from cnpm/issue233-remove-readme
Issue233 remove readme
2014-02-28 23:25:08 +08:00
dead_horse
03ef728049 default readme to null, fixed #233 2014-02-28 22:37:45 +08:00
dead_horse
f00ed85d41 remove readme in versions 2014-02-28 22:32:44 +08:00
fengmk2
685af2a367 Release 0.3.3 2014-02-28 17:41:09 +08:00
dead_horse
818f216fb4 Merge pull request #232 from cnpm/host-hotfix
get request host from request.headers
2014-02-28 16:36:44 +08:00
fengmk2
1b47495565 get request host from request.headers 2014-02-28 16:35:51 +08:00
dead_horse
0ab314f27c Merge pull request #231 from cnpm/bug-fix
fix deps display bug#230 and nsf.url TypeError#229
2014-02-28 15:40:04 +08:00
fengmk2
95076c8787 fix deps display bug#230 and nsf.url TypeError#229 2014-02-28 15:35:37 +08:00
fengmk2
4e6eb0a9cc Release 0.3.2 2014-02-28 14:46:17 +08:00
fengmk2
29f17dd5d1 Merge pull request #228 from cnpm/update-sess
update koa-sess and koa-redis
2014-02-28 14:42:03 +08:00
dead_horse
3a48637ef1 update koa-sess and koa-redis 2014-02-28 14:40:47 +08:00
fengmk2
e1029b005f Merge pull request #227 from cnpm/fix-nfs
Fix nfs
2014-02-28 12:20:43 +08:00
dead_horse
66771dfc3b fix sync all test 2014-02-28 12:15:30 +08:00
dead_horse
66f05a2f07 remove nfs.downloadStream first, fix tmppath error 2014-02-28 12:04:28 +08:00
fengmk2
6660cabbb6 Merge pull request #226 from cnpm/giturl-bug-fix
fix fengmk2/giturl#1 bug
2014-02-27 20:53:33 +08:00
fengmk2
fbe1971957 fix fengmk2/giturl#1 bug 2014-02-27 20:52:19 +08:00
fengmk2
d75c3877bb Release 0.3.1 2014-02-27 15:06:17 +08:00
dead_horse
564ec488ea Merge pull request #225 from cnpm/etag
add etag fixed #224
2014-02-27 15:00:26 +08:00
fengmk2
1559c16c3d add etag fixed #224 2014-02-27 14:58:08 +08:00
fengmk2
2345536cbd travis ci install on source npm 2014-02-27 10:16:03 +08:00
fengmk2
89808e398b Release 0.3.0 2014-02-27 10:13:05 +08:00
fengmk2
5ddf238c08 fix typo and dont sync not exists packages 2014-02-27 10:12:53 +08:00
dead_horse
1ae193e306 Merge pull request #221 from cnpm/koa-merge
Use koa instead of connect
2014-02-27 09:57:54 +08:00
dead_horse
9d511c326c use koa-middlewares
contain some frequently-used middlewares in this module
only need to maintain this module
2014-02-27 09:51:44 +08:00
fengmk2
78d7a77b0d fix signed cookie not work on npm@1.3.25; node --harmony-generators 2014-02-27 09:51:44 +08:00
fengmk2
0f494822bc fix opensearch test case 2014-02-27 09:51:44 +08:00
fengmk2
211df84514 update koa bodyparser 2014-02-27 09:51:44 +08:00
fengmk2
fee243726e logger.error(err) should send err stack email notice 2014-02-27 09:51:44 +08:00
fengmk2
52e7e6d069 json body parse limit and bug fix.
* override json limit to default 10mb. fixed #209
 * fix #210 addPackageAndDist package version detect bug
2014-02-27 09:51:42 +08:00
dead_horse
551ae832e3 fix sync 404 reason not clear 2014-02-27 09:51:07 +08:00
dead_horse
9af99f4af2 all controllers to koa 2014-02-27 09:51:07 +08:00
dead_horse
3e8ecda9e4 controller/web/user.js to koa 2014-02-27 09:51:07 +08:00
dead_horse
fb744176f8 change web connect to koa 2014-02-27 09:51:06 +08:00
dead_horse
5e1ab4356d use outputError 2014-02-27 09:51:06 +08:00
dead_horse
5fb9a007f9 use yield exports.addPackageAndDist.call(this, next); 2014-02-27 09:51:06 +08:00
dead_horse
780a5aa158 add end() when ws write end 2014-02-27 09:51:06 +08:00
dead_horse
ab2ff4ed9e fix yield coWrite 2014-02-27 09:51:06 +08:00
dead_horse
2dad7553e6 fix all the test of registry module.test.js 2014-02-27 09:51:05 +08:00
dead_horse
acfa2e418b convert registry/module.js to koa type 2014-02-27 09:51:03 +08:00
fengmk2
74101fda7a fix auth middleware 2014-02-27 09:48:21 +08:00
fengmk2
2ec1eec91c finish registry user controller koa and update mm to support thunkify. fixed #196 2014-02-27 09:48:20 +08:00
fengmk2
b3e966184a change controllers/user.js to koa 2014-02-27 09:48:20 +08:00
dead_horse
b09960858c thunkify all proxy 2014-02-27 09:48:20 +08:00
dead_horse
84634af0c2 convert all middlewares to koa type 2014-02-27 09:48:20 +08:00
dead_horse
d60d7eaf2e change regsitry sync to koa 2014-02-27 09:48:18 +08:00
dead_horse
6d6a994997 addd koa-jsonp, koa-bodyparser, fix / controller 2014-02-27 09:46:42 +08:00
fengmk2
ab564c3b32 first koa run registry home page / 2014-02-27 09:46:39 +08:00
fengmk2
55b836388d Merge pull request #212 from cnpm/fix-sync-404
return friendly 404 reason
2014-02-26 08:56:32 +08:00
dead_horse
3f45384b74 return friendly 404 reason 2014-02-26 00:21:48 +08:00
dead_horse
6fe2997fb5 Merge pull request #211 from cnpm/bug-fix
Bug fix
2014-02-25 22:29:26 +08:00
fengmk2
cdd857ca2d override json limit to default 10mb. fixed #209 2014-02-25 20:57:54 +08:00
fengmk2
c9e513350a fix #210 addPackageAndDist package version detect bug 2014-02-25 20:56:53 +08:00
fengmk2
7b2cbd6d1d Release 0.2.27 2014-02-19 18:10:50 +08:00
fengmk2
90959ba34f Merge pull request #193 from cnpm/issue189-search-api
support json result in search, fixed #189
2014-02-19 17:42:14 +08:00
dead_horse
0f6b6a2f2b support json result in search, fixed #189 2014-02-19 17:40:44 +08:00
fengmk2
666d98d86e Release 0.2.26 2014-02-19 16:28:57 +08:00
dead_horse
e244efb153 Merge pull request #192 from cnpm/publish-add-deps
npm publish also need to add deps
2014-02-19 15:29:04 +08:00
fengmk2
67d824e5dc npm publish also need to add deps 2014-02-19 15:23:02 +08:00
fengmk2
daf29f760d Release 0.2.25 2014-02-19 14:23:11 +08:00
dead_horse
e5939d170f Merge pull request #191 from cnpm/module_deps
Dependents support. fixed #190
2014-02-19 14:15:01 +08:00
fengmk2
3526c9eff7 max handle number of package.json dependencies property 2014-02-19 14:07:06 +08:00
fengmk2
973889c73a Dependents support. fixed #190 2014-02-19 13:29:33 +08:00
214 changed files with 17361 additions and 5585 deletions

8
.gitignore vendored
View File

@@ -7,6 +7,7 @@ coverage.html
*.pid
*.gz
dump.rdb
.DS_Store
pids
logs
@@ -15,9 +16,16 @@ results
node_modules
npm-debug.log
public/dist/
.dist
config/config.js
backup/*.json
backup/*.gz
docs/web/history.md
docs/web/_readme.md
view/web/_layout.html
bin/mysql.js
bin/test.sql
coverage/
config/web_readme.md
.tmp/
*.sqlite

4
.jshintignore Normal file
View File

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

95
.jshintrc 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" : true, // 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" : true, // 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

@@ -8,5 +8,16 @@ logo.png
public/dist/
backup/*.json
backup/*.gz
docs/web/history.md
docs/web/_readme.md
view/web/_layout.html
bin/mysql.js
bin/test.sql
coverage/
.jshintrc
.jshintignore
.DS_Store
config/web_readme.md
.dist/
config/config.js
*.sqlite

View File

@@ -1,5 +1,6 @@
language: node_js
node_js:
- '0.10'
install: make install
script: make test-coveralls
- '0.11.14'
before_install: "npm install --build-from-source"
script: "make test-travis-all"
after_script: "npm install coveralls@2 && cat ./coverage/lcov.info | coveralls"

View File

@@ -1,5 +1,5 @@
# Ordered by date of first contribution.
# Auto-generated by 'contributors' on Fri, 24 Jan 2014 08:35:59 GMT.
# Auto-generated by 'contributors' on Mon, 03 Mar 2014 13:01:28 GMT.
# https://github.com/xingrz/node-contributors
fengmk2 <fengmk2@gmail.com> (https://github.com/fengmk2)

39
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,39 @@
# How to contribute
Third-party patches are essential for keeping `cnpmjs.org` great.
We want to keep it as easy as possible to contribute changes that
get things working in your environment. There are a few guidelines that we
need contributors to follow so that we can have a chance of keeping on
top of things.
## Getting Started
* Make sure you have a [GitHub account](https://github.com/signup/free)
* Fork the repository on GitHub
## Making Changes
* Create a topic branch from where you want to base your work.
* This is usually the master branch.
* Only target release branches if you are certain your fix must be on that
branch.
* To quickly create a topic branch based on master.
Please avoid working directly on the `master` branch.
* Make commits of logical units and including unit tests.
* Check for unnecessary whitespace with `git diff --check` before committing.
* Make sure your commit messages are in the proper format.
* Make sure you have added the necessary tests for your changes.
* Run _all_ the tests to assure nothing else was accidentally broken.
* Follow [node style guide](https://github.com/felixge/node-style-guide)
## Submitting Changes
* Push your changes to a topic branch in your fork of the repository.
* Submit a pull request.
* Make sure travis-ci test pass.
# Additional Resources
* [General GitHub documentation](http://help.github.com/)
* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
* [cnpmjs.org](http://cnpmjs.org)

View File

@@ -1,131 +1,689 @@
0.2.24 / 2014-02-13
2.0.0-beta0 / 2014-11-02
==================
* ungrade koa-markdown to use remarkable, close #482
* fix(module_log): limit module sync log size to 1MB
* refactor(config): remove adaptScope config key
* chore(Makefile): $ make install-production
* fix(sequelize): show warnning message when using old config.js
* docs(readme): Migrating from 1.x to 2.x
* feat(sync): add min sync interval time detect
* refactor(dispatch): remove unused codes
* use sequelize to connect database
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](https://github.com/cnpm/cnpmjs.org/issues/336)
* bump thunkify-wrap
* bump koa-middlewares
* remove outputError
* bump dependencies
* use svg badge
* add package/notfound page
* add dist mirror link to home page
* fix sync listdiff and add more test cases
0.5.0 / 2014-05-13
==================
* filter /nightlies/*
* use koa setter instead of set()
* add more info on error email
* add sync dist to sync/index.js
* show dist page
* sync dist file and save it to database
* disable gzip before [#335](https://github.com/cnpm/cnpmjs.org/issues/335) has fix
0.4.3 / 2014-04-18
==================
* 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](https://github.com/cnpm/cnpmjs.org/issues/333) from cnpm/issue332-tag
* fix space
* 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](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](https://github.com/cnpm/cnpmjs.org/issues/325)
* refator sync
* 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
* fix redis detect logic
0.4.1 / 2014-04-10
==================
* fix sync status code error
0.4.0 / 2014-04-09
==================
* fix test cases to run on local machine
* add contribute guidelines
* 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](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](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
==================
* 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](https://github.com/cnpm/cnpmjs.org/issues/291)
* refine docs foloder
* 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
==================
* use common.isMaintainer, fixed [#283](https://github.com/cnpm/cnpmjs.org/issues/283)
* update dependencies
* 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
==================
* 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](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](https://github.com/cnpm/cnpmjs.org/issues/271)
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](https://github.com/cnpm/cnpmjs.org/issues/267)
0.3.8 / 2014-03-11
==================
* update middlewares, fixed missing charset bug [#264](https://github.com/cnpm/cnpmjs.org/issues/264)
0.3.7 / 2014-03-11
==================
* show worker die date time
* update to koa@0.5.1
* hotfix for star user
* fix yield gather, sync missing deps even no missing versions
* fix return versions
* fix makefile, remove eventproxy
* refactor sync_module_worker
* 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
==================
* 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
==================
* 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
==================
* add cov
* use istanbul run test coverage
* gzip support. fix [#241](https://github.com/cnpm/cnpmjs.org/issues/241)
* readme spelling patch (@stanzheng)
* default readme to null, fixed [#233](https://github.com/cnpm/cnpmjs.org/issues/233)
* remove readme in versions
0.3.3 / 2014-02-28
==================
* 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](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
==================
* update koa-sess and koa-redis
* fix sync all test
* remove nfs.downloadStream first, fix tmppath error
* fix fengmk2/giturl[#1](https://github.com/cnpm/cnpmjs.org/issues/1) bug
0.3.1 / 2014-02-27
==================
* add etag fixed [#224](https://github.com/cnpm/cnpmjs.org/issues/224)
* travis ci install on source npm
0.3.0 / 2014-02-27
==================
* fix typo and dont sync not exists pkgs
* use koa-middlewares
* fix signed cookie not work on npm@1.3.25; node --harmony-generators
* fix opensearch test case
* update koa bodyparser
* logger.error(err) should send err stack email notice
* json body parse limit and bug fix.
* fix sync 404 reason not clear
* all controllers to koa
* controller/web/user.js to koa
* change web connect to koa
* use outputError
* use yield exports.addPackageAndDist.call(this, next);
* add end() when ws write end
* fix yield coWrite
* 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](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](https://github.com/cnpm/cnpmjs.org/issues/212) from cnpm/fix-sync-404
* return friendly 404 reason
* 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
==================
* support json result in search, fixed [#189](https://github.com/cnpm/cnpmjs.org/issues/189)
0.2.26 / 2014-02-19
==================
* npm publish also need to add deps
0.2.25 / 2014-02-19
==================
* max handle number of package.json `dependencies` property
* Dependents support. fixed [#190](https://github.com/cnpm/cnpmjs.org/issues/190)
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
@@ -135,23 +693,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
@@ -163,78 +721,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
@@ -243,15 +801,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
@@ -262,9 +820,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,38 +1,103 @@
TESTS = $(shell ls -S `find test -type f -name "*.test.js" -print`)
REPORTER = tap
REPORTER = spec
TIMEOUT = 30000
MOCHA_OPTS =
REGISTRY = --registry=https://registry.npm.taobao.org
DB = sqlite
install:
@npm install --registry=http://registry.cnpmjs.org --cache=${HOME}/.npm/.cache/cnpm --disturl=http://dist.u.qiniudn.com
@npm install --build-from-source $(REGISTRY) \
--disturl=https://npm.taobao.org/dist
test: install
@NODE_ENV=test ./node_modules/mocha/bin/mocha \
install-production production:
@NODE_ENV=production $(MAKE) install
jshint: install
@-node_modules/.bin/jshint ./
init-database:
@node --harmony test/init_db.js
init-mysql:
@mysql -uroot -e 'DROP DATABASE IF EXISTS cnpmjs_test;'
@mysql -uroot -e 'CREATE DATABASE cnpmjs_test;'
test: install init-database
@NODE_ENV=test DB=${DB} node_modules/.bin/mocha \
--harmony \
--reporter $(REPORTER) \
--timeout $(TIMEOUT) \
--require should \
--require should-http \
--require co-mocha \
--require ./test/init.js \
$(MOCHA_OPTS) \
$(TESTS)
test-cov:
@$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=travis-cov
test-sqlite:
@$(MAKE) test DB=sqlite
test-cov-html:
@rm -f coverage.html
@$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=html-cov > coverage.html
@ls -lh coverage.html
test-mysql: init-mysql
@$(MAKE) test DB=mysql
test-coveralls: test
@echo TRAVIS_JOB_ID $(TRAVIS_JOB_ID)
@-$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=mocha-lcov-reporter | ./node_modules/coveralls/bin/coveralls.js
test-all: test-sqlite test-mysql
test-all: test test-cov
test-cov cov: install init-database
@NODE_ENV=test DB=${DB} node --harmony \
node_modules/.bin/istanbul cover --preserve-comments \
node_modules/.bin/_mocha \
-- -u exports \
--reporter $(REPORTER) \
--timeout $(TIMEOUT) \
--require should \
--require should-http \
--require co-mocha \
--require ./test/init.js \
$(MOCHA_OPTS) \
$(TESTS)
test-cov-sqlite:
@$(MAKE) test-cov DB=sqlite
test-cov-mysql: init-mysql
@$(MAKE) test-cov DB=mysql
test-travis: install init-database
@NODE_ENV=test DB=${DB} 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)
test-travis-sqlite:
@$(MAKE) test-travis DB=sqlite
test-travis-mysql: init-mysql
@$(MAKE) test-travis DB=mysql
test-travis-all: test-travis-sqlite test-travis-mysql
dev:
@node_modules/.bin/node-dev --harmony dispatch.js
contributors: install
@./node_modules/contributors/bin/contributors -f plain -o AUTHORS
@node_modules/.bin/contributors -f plain -o AUTHORS
autod: install
@./node_modules/.bin/autod -w -e public,view,docs,backup
@node_modules/.bin/autod -w \
--prefix "~" \
--exclude public,view,docs,backup,coverage \
--dep bluebird,mysql \
--devdep mocha,should,istanbul-harmony,jshint
@$(MAKE) install
.PHONY: test

120
README.md
View File

@@ -1,55 +1,119 @@
cnpmjs.org
=======
[![Build Status](https://secure.travis-ci.org/cnpm/cnpmjs.org.png)](http://travis-ci.org/cnpm/cnpmjs.org) [![Coverage Status](https://coveralls.io/repos/cnpm/cnpmjs.org/badge.png)](https://coveralls.io/r/cnpm/cnpmjs.org) [![Dependency Status](https://gemnasium.com/cnpm/cnpmjs.org.png)](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.png?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)
## What is this?
Private npm registry and web for Enterprise, base on MySQL and [Simple Store Service](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide).
Private npm registry and web for Enterprise, base on [koa](http://koajs.com/),
MySQL and [Simple Store Service](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide).
@[JacksonTian](https://github.com/JacksonTian/) had a talk about [private npm](https://speakerdeck.com/jacksontian/qi-ye-ji-node-dot-jskai-fa).
Our goal is to provide a low cost maintenance and easy to use solution for private npm.
![cnpm](https://docs.google.com/drawings/d/12QeQfGalqjsB77mRnf5Iq5oSXHCIUTvZTwECMonqCmw/pub?w=480&h=360)
## What can you do with `cnpmjs.org`
* Build a private npm for your own enterprise. ([alibaba](http://www.alibaba.com/) is using `cnpmjs.org` now)
* Build a mirror NPM. (we use it to build a mirror in China: [cnpmjs.org](http://cnpmjs.org/))
* Build a completely independent NPM registry to store whatever you like.
## Install
### Features
```bash
$ npm install
```
* **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, MariaDB, SQLite or PostgreSQL databases,
tarball(tgz file) store in CDN or other store systems.
* **Automatic synchronization**: automatic synchronization from any registry specified, support two sync modes:
- Sync all modules from a specified registry, like [npm registry](http://registry.npmjs.org).
- Only sync the modules that exists in your own registry.
* **Manual synchronization**: automatic synchronization may has little delay, but you can syn immediately by manually.
* **Customized client**: we provide a client [cnpm](https://github.com/cnpm/cnpm)
to extend `npm` with more features(`sync` command, [gzip](https://github.com/npm/npm-registry-client/pull/40) support).
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)
## Usage
**PROTIP** Be sure to read [Migrating from 1.x to 2.x](https://github.com/cnpm/cnpmjs.org/wiki/Migrating-from-1.x-to-2.x)
as well as [New features in 2.x](https://github.com/cnpm/cnpmjs.org/wiki/New-features-in-2.x).
```js
$ node dispatch.js
```
## Guide
## Getting Start
* @[dead-horse](https://github.com/dead-horse): [What is cnpm?](http://deadhorse.me/slides/cnpmjs.html)
* install and deploy cnpmjs.org through npm: [examples](https://github.com/cnpm/custom-cnpm-example)
* 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)
* [wiki](https://github.com/cnpm/cnpmjs.org/wiki)
## Authors
## Develop on your local machine
### Dependencies
* [node](http://nodejs.org) >=0.11.12, use `--harmony`
* Databases: only required one type
* [sqlite3](https://npm.taobao.org/package/sqlite3) >= 3.0.2, we use `sqlite3` by default
* [MySQL](http://dev.mysql.com/downloads/) >= 0.5.0, include `mysqld` and `mysql cli`. I test on `mysql@5.6.16`.
* MariaDB
* PostgreSQL
### Clone codes and run test
```bash
$ git summary
# clone from git
$ git clone https://github.com/cnpm/cnpmjs.org.git
project : cnpmjs.org
repo age : 7 weeks
active : 132 days
commits : 315
files : 88
authors :
190 fengmk2 60.3%
122 dead_horse 38.7%
2 4simple 0.6%
1 Alsotang 0.3%
# install dependencies
$ make install
# test
$ make test
# coverage
$ make test-cov
# udpate dependencies
$ make autod
# start server with development mode
$ make dev
```
## How to contribute
* Clone the project
* Checkout a new branch
* Add new features or fix bugs in the new branch
* Make a pull request and we will review it ASAP
Tips: make sure your code is following the [node-style-guide](https://github.com/felixge/node-style-guide).
## License
(The MIT License)

View File

@@ -7,7 +7,7 @@ export NODE_ENV='production'
ulimit -c unlimited
cd `dirname $0`/..
NODEJS=node
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

@@ -0,0 +1,74 @@
/**!
* cnpmjs.org - bin/restore_module_deps.js
*
* Copyright(c) 2014
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
"use strict";
/**
* Module dependencies.
*/
var mysql = require('../common/mysql');
var Module = require('../proxy/module');
var ModuleDeps = require('../proxy/module_deps');
var addCount = 0;
function restore(id, callback) {
var sql = 'SELECT id, name, package FROM module WHERE id > ? ORDER BY id ASC LIMIT 1000';
mysql.query(sql, [id], function (err, rows) {
if (err) {
return callback(err);
}
if (rows.length === 0) {
return callback(null, []);
}
console.log('[%s] got %d rows', id, rows.length);
rows.forEach(function (r) {
Module.parseRow(r);
if (!r.package) {
return;
}
var deps = Object.keys(r.package.dependencies || {});
if (!Array.isArray(deps) || !deps.length) {
return;
}
deps.forEach(function (dep) {
ModuleDeps.add(dep, r.name, function (err) {
// console.log('[%s] add %s <= %s, error: %s', id, dep, r.name, err);
});
});
addCount += deps.length;
});
setTimeout(function () {
console.log('[%s] add %d relations', id, addCount);
callback(null, rows);
}, 1000);
});
}
var id = 0;
function run() {
restore(id, function (err, rows) {
if (err) {
throw err;
}
if (rows.length === 0) {
console.log('finished, last id: %s, exit.', id);
process.exit(0);
}
id = rows[rows.length - 1].id;
run();
});
}
run();

View File

@@ -1,7 +1,12 @@
/*!
/**!
* cnpmjs.org - common/logger.js
* Copyright(c) 2013
* Author: dead_horse <undefined>
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
@@ -10,61 +15,48 @@
* Module dependencies.
*/
var config = require('../config');
var formater = require('error-formater');
var Logger = require('mini-logger');
var utility = require('utility');
var util = require('util');
var moment = require('moment');
var logstream = require('logfilestream');
var ms = require('ms');
var config = require('../config');
var mail = require('./mail');
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');
if (msg instanceof Error) {
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) {
if (config.debug) {
var level = catetory;
console.log('[' + level + '] ' + msg);
} else {
stream.write(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

@@ -1,4 +1,4 @@
/*!
/**!
* cnpmjs.org - common/mail.js
*
* Copyright(c) cnpmjs.org and other contributors.
@@ -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

@@ -1,71 +0,0 @@
/*!
* cnpmjs.org - common/mysql.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com>
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var ready = require('ready');
var mysql = require('mysql');
var config = require('../config');
var server = config.mysqlServers[0];
// TODO: query timeout
var pool = mysql.createPool({
host: server.host,
port: server.port,
user: server.user,
password: server.password,
database: config.mysqlDatabase,
connectionLimit: config.mysqlMaxConnections,
multipleStatements: true,
});
exports.pool = pool;
exports.query = function (sql, values, cb) {
pool.query(sql, values, cb);
};
exports.queryOne = function (sql, values, cb) {
if (typeof values === 'function') {
cb = values;
values = null;
}
exports.query(sql, values, function (err, rows) {
if (rows) {
rows = rows[0];
}
cb(err, rows);
});
};
exports.escape = function (val) {
return pool.escape(val);
};
ready(exports);
function init() {
exports.query('show tables', function (err, rows) {
if (err) {
console.error('[%s] [worker:%s] mysql init error: %s', Date(), process.pid, err);
setTimeout(init, 1000);
return;
}
console.log('[%s] [worker:%s] mysql ready, got %d tables', Date(), process.pid, rows.length);
exports.ready(true);
});
}
init();

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,63 +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 qn = require('qn');
var config = require('../config');
var client = qn.create(config.qn);
exports._client = client;
/**
* Upload file
*
* @param {String} filepath
* @param {Object} options
* - {String} key
* - {Number} size
* @param {Function(err, result)} callback
* - {Object} result
* - {String} url
*/
exports.upload = function (filepath, options, callback) {
client.delete(options.key, function (err, data) {
client.uploadFile(filepath, {key: options.key, size: options.size}, function (err, data) {
if (err) {
return callback(err);
}
callback(null, {url: data.url});
});
});
};
exports.uploadBuffer = function (buf, options, callback) {
client.delete(options.key, function (err, data) {
client.upload(buf, {key: options.key}, function (err, data) {
if (err) {
return callback(err);
}
callback(null, {url: data.url});
});
});
};
exports.url = function (key) {
return client.resourceURL(key);
};
exports.remove = function (key, callback) {
client.delete(key, callback);
};

37
common/redis.js Normal file
View File

@@ -0,0 +1,37 @@
/**!
* cnpmjs.org - common/redis.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
'use strict';
/**
* Module dependencies.
*/
var config = require('../config');
// close redis by set config.redis to `null` or `{}`
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.port, config.redis.host);
_client.on('error', function (err) {
logger.error(err);
});
module.exports = wrapper(_client);
} else {
console.warn('[%s] [worker:%s:common/redis.js] Redis config can not found',
Date(), process.pid);
module.exports = null;
}

75
common/sequelize.js Normal file
View File

@@ -0,0 +1,75 @@
/**!
* cnpmjs.org - common/sequelize.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var Sequelize = require('sequelize');
var DataTypes = require('sequelize/lib/data-types');
var config = require('../config');
if (config.mysqlServers && config.database.dialect === 'sqlite') {
// https://github.com/cnpm/cnpmjs.org/wiki/Migrating-from-1.x-to-2.x
// forward compat with old style on 1.x
// mysqlServers: [
// {
// host: '127.0.0.1',
// port: 3306,
// user: 'root',
// password: ''
// }
// ],
// mysqlDatabase: 'cnpmjs_test',
// mysqlMaxConnections: 4,
// mysqlQueryTimeout: 5000,
console.warn('[WARNNING] your config.js was too old, please @see https://github.com/cnpm/cnpmjs.org/wiki/Migrating-from-1.x-to-2.x');
var server = config.mysqlServers[0];
config.database = {
db: config.mysqlDatabase,
username: server.user,
password: server.password,
dialect: 'mysql',
host: server.host,
port: server.port,
pool: {
maxConnections: config.mysqlMaxConnections || 10,
minConnections: 0,
maxIdleTime: 30000
},
logging: !!process.env.SQL_DEBUG,
};
}
var database = config.database;
// sync database before app start, defaul is false
database.syncFirst = false;
// add longtext for mysql
Sequelize.LONGTEXT = DataTypes.LONGTEXT = DataTypes.TEXT;
if (config.dialect === 'mysql') {
Sequelize.LONGTEXT = DataTypes.LONGTEXT = 'LONGTEXT';
}
database.define = {
timestamps: true,
createdAt: 'gmt_create',
updatedAt: 'gmt_modified',
charset: 'utf8',
collate: 'utf8_general_ci',
};
var sequelize = new Sequelize(database.db, database.username, database.password, database);
module.exports = sequelize;

View File

@@ -1,41 +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 connect = require('connect');
var RedisStore = require('connect-redis')(connect);
var config = require('../config');
var session;
var key = 'AuthSession';
var cookie = { path: '/', httpOnly: true, maxAge: 3600000 * 24 * 30 };
if (config.debug) {
session = connect.cookieSession({
secret: config.sessionSecret,
key: key,
cookie: cookie
});
} else {
session = connect.session({
key: key,
secret: config.sessionSecret,
store: config.sessionStore || new RedisStore(config.redis),
cookie: cookie,
});
}
module.exports = session;

35
common/urllib.js Normal file
View File

@@ -0,0 +1,35 @@
/**!
* 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;

View File

@@ -1,4 +1,4 @@
/*!
/**!
* cnpmjs.org - config/index.js
*
* Copyright(c) cnpmjs.org and other contributors.
@@ -17,86 +17,228 @@
var path = require('path');
var fs = require('fs');
var os = require('os');
var mkdirp = require('mkdirp');
var copy = require('copy-to');
fs.existsSync = fs.existsSync || path.existsSync;
var pkg = require('../package.json');
var version = require('../package.json').version;
var root = path.dirname(__dirname);
var config = {
version: pkg.version,
version: version,
/**
* Cluster mode
*/
enableCluster: false,
numCPUs: os.cpus().length,
/*
* server configure
*/
registryPort: 7001,
webPort: 7002,
enableCluster: false,
debug: true, // if debug
logdir: path.join(root, '.tmp', 'logs'),
viewCache: false,
// mysql config
mysqlServers: [
{
host: 'keydiary.mysql.rds.aliyuncs.com', // 'db4free.net'
port: 3306,
user: 'cnpmjs',
password: 'cnpmjs123'
}
],
mysqlDatabase: 'cnpmjstest',
mysqlMaxConnections: 4,
mysqlQueryTimeout: 5000,
bindingHost: '127.0.0.1', // only binding on 127.0.0.1 for local access
// 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',
redis: {
host: 'pub-redis-19533.us-east-1-4.3.ec2.garantiadata.com',
port: 19533,
pass: 'cnpmjs_dev'
},
uploadDir: path.join(root, 'public', 'dist'),
// qiniu cdn: http://www.qiniu.com/, it free for dev.
qn: {
accessKey: "iN7NgwM31j4-BZacMjPrOQBs34UG1maYCAQmhdCV",
secretKey: "6QTOr2Jg1gcZEWDQXKOGZh5PziC2MCV5KsntT70j",
bucket: "qtestbucket",
domain: "http://qtestbucket.qiniudn.com"
// 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,
// 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',
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
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: 'http://ww4.sinaimg.cn/large/69c1d4acgw1ebfly5kjlij208202oglr.jpg',
registryHost: 'r.cnpmjs.org',
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
sourceNpmRegistry: 'http://registry.npmjs.org',
enablePrivate: true, // enable private mode, only admin can publish, other use just can sync package from source npm
admins: {
admin: 'admin@cnpmjs.org',
fengmk2: 'fengmk2@gmail.com',
dead_horse: 'dead_horse@qq.com',
cnpmjstest10: 'cnpmjstest10@cnpmjs.org',
// max handle number of package.json `dependencies` property
maxDependencies: 200,
// backup filepath prefix
backupFilePrefix: '/cnpm/backup/',
/**
* database config
*/
database: {
db: 'cnpmjs_test',
username: 'root',
password: '',
// the sql dialect of the database
// - currently supported: 'mysql', 'sqlite', 'postgres', 'mariadb'
dialect: 'sqlite',
// custom host; default: 127.0.0.1
host: '127.0.0.1',
// custom port; default: 3306
port: 3306,
// use pooling in order to reduce db connection overload and to increase speed
// currently only for mysql and postgresql (since v1.5.0)
pool: {
maxConnections: 10,
minConnections: 0,
maxIdleTime: 30000
},
// the storage engine for 'sqlite'
// default store into ~/cnpmjs.org.sqlite
storage: path.join(process.env.HOME || root, 'cnpmjs.org.sqlite'),
logging: !!process.env.SQL_DEBUG,
},
// 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'
],
// 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: [],
/**
* sync configs
*/
// sync dist config
// sync node.js dist from nodejs.org
noticeSyncDistError: true,
disturl: 'http://nodejs.org/dist',
syncDist: false,
// 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,
backupFilePrefix: '/cnpm/backup/', // backup filepath prefix
// 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
var customConfig = path.join(root, 'config/config.js');
if (fs.existsSync(customConfig)) {
var options = require(customConfig);
for (var k in options) {
config[k] = options[k];
}
copy(require(customConfig)).override(config);
}
mkdirp.sync(config.logdir);
@@ -108,7 +250,5 @@ config.loadConfig = function (customConfig) {
if (!customConfig) {
return;
}
for (var key in customConfig) {
config[key] = customConfig[key];
}
copy(customConfig).override(config);
};

View File

@@ -1,76 +0,0 @@
/**!
* cnpmjs.org - controllers/download.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var moment = require('moment');
var DownloadTotal = require('../proxy/download');
exports.total = function (name, callback) {
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 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');
var thisweekStart = end.clone().startOf('week').format('YYYY-MM-DD');
start = start.format('YYYY-MM-DD');
end = end.format('YYYY-MM-DD');
lastweekStart = lastweekStart.format('YYYY-MM-DD');
var method = name ? 'getModuleTotal' : 'getTotal';
var args = [start, end];
if (name) {
args.unshift(name);
}
args.push(function (err, rows) {
if (err) {
return callback(err);
}
var download = {
today: 0,
thisweek: 0,
thismonth: 0,
lastday: 0,
lastweek: 0,
lastmonth: 0,
};
for (var i = 0; i < rows.length; i++) {
var r = rows[i];
if (r.date === end) {
download.today += r.count;
}
if (r.date >= thismonthStart) {
download.thismonth += r.count;
}
if (r.date >= thisweekStart) {
download.thisweek += r.count;
}
if (r.date === lastday) {
download.lastday += r.count;
}
if (r.date >= lastweekStart && r.date <= lastweekEnd) {
download.lastweek += r.count;
}
if (r.date >= start && r.date <= lastmonthEnd) {
download.lastmonth += r.count;
}
}
callback(null, download);
});
DownloadTotal[method].apply(DownloadTotal, args);
};

View File

@@ -1,898 +0,0 @@
/**!
* cnpmjs.org - controllers/registry/module.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:controllers:registry:module');
var path = require('path');
var fs = require('fs');
var crypto = require('crypto');
var utility = require('utility');
var eventproxy = require('eventproxy');
var Bagpipe = require('bagpipe');
var urlparse = require('url').parse;
var mime = require('mime');
var semver = require('semver');
var ms = require('ms');
var config = require('../../config');
var Module = require('../../proxy/module');
var Total = require('../../proxy/total');
var nfs = require('../../common/nfs');
var common = require('../../lib/common');
var Log = require('../../proxy/module_log');
var DownloadTotal = require('../../proxy/download');
var SyncModuleWorker = require('../../proxy/sync_module_worker');
var logger = require('../../common/logger');
exports.show = function (req, res, next) {
var name = req.params.name;
var ep = eventproxy.create();
ep.fail(next);
Module.listTags(name, ep.done('tags'));
Module.listByName(name, ep.done('rows'));
ep.all('tags', 'rows', function (tags, rows) {
debug('show module, user: %s, allowSync: %s, isAdmin: %s', req.session.name, req.session.allowSync, req.session.isAdmin);
// if module not exist in this registry,
// sync the module backend and return package info from official registry
// TODO: Need to change this logic, it will cause unpublish sync again.
// TODO: Hard to detect this show is on install flow or unpublish flow
if (rows.length === 0) {
if (!req.session.allowSync) {
return next();
}
var username = (req.session && req.session.name) || 'anonymous';
SyncModuleWorker.sync(name, username, function (err, result) {
if (err) {
return next(err);
}
if (!result.ok) {
return res.json(result.statusCode, result.pkg);
}
res.json(200, result.pkg);
});
return;
}
var nextMod = null;
var latestMod = null;
var distTags = {};
for (var i = 0; i < tags.length; i++) {
var t = tags[i];
distTags[t.tag] = t.version;
}
var versions = {};
var times = {};
var attachments = {};
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
if (row.version === 'next') {
nextMod = row;
continue;
}
var pkg = row.package;
common.setDownloadURL(pkg, req);
pkg._cnpm_publish_time = row.publish_time;
versions[pkg.version] = pkg;
times[pkg.version] = row.publish_time ? new Date(row.publish_time) : row.gmt_modified;
if ((!distTags.latest && !latestMod) || distTags.latest === row.version) {
latestMod = row;
}
}
if (!latestMod) {
latestMod = nextMod || rows[0];
}
if (!nextMod) {
nextMod = latestMod;
}
var rev = '';
if (nextMod) {
rev = String(nextMod.id);
}
var info = {
_id: name,
_rev: rev,
name: name,
description: latestMod.package.description,
"dist-tags": distTags,
maintainers: latestMod.package.maintainers,
time: times,
author: latestMod.package.author,
repository: latestMod.package.repository,
versions: versions,
readme: latestMod.package.readme,
_attachments: attachments,
};
debug('show module %s: %s, latest: %s', name, rev, latestMod.version);
res.json(info);
});
};
exports.get = function (req, res, next) {
var name = req.params.name;
var tag = req.params.version;
var version = semver.valid(tag);
var ep = eventproxy.create();
ep.fail(next);
var method = version ? 'get' : 'getByTag';
var queryLabel = version ? version : tag;
Module[method](name, queryLabel, ep.done(function (mod) {
if (mod) {
common.setDownloadURL(mod.package, req);
mod.package._cnpm_publish_time = mod.publish_time;
return res.json(mod.package);
}
ep.emit('notFound');
}));
ep.once('notFound', function () {
if (!req.session.allowSync) {
return next();
}
var username = (req.session && req.session.username) || 'anonymous';
SyncModuleWorker.sync(name, username, function (err, result) {
if (err) {
return next(err);
}
var pkg = result.pkg && result.pkg.versions[version];
if (!pkg) {
return res.json(404, {
error: 'not exist',
reason: 'version not found: ' + version
});
}
res.json(pkg);
});
});
};
var _downloads = {};
var DOWNLOAD_TIMEOUT = ms('10m');
exports.download = function (req, res, next) {
var name = req.params.name;
var filename = req.params.filename;
var version = filename.slice(name.length + 1, -4);
var ep = eventproxy.create();
ep.fail(next);
Module.get(name, version, ep.doneLater('moduleInfo'));
ep.once('moduleInfo', function (row) {
if (!row || !row.package || !row.package.dist) {
return ep.emit('nodist');
}
var dist = row.package.dist;
if (dist.key) {
return ep.emit('key', dist);
} else {
return ep.emit('url', dist);
}
ep.emit('nodist');
});
ep.once('nodist', function () {
if (!nfs.url) {
return next();
}
ep.emit('url', nfs.url(common.getCDNKey(name, filename)));
});
ep.once('url', function (dist) {
res.statusCode = 302;
res.setHeader('Location', dist.tarball);
res.end();
_downloads[name] = (_downloads[name] || 0) + 1;
});
ep.once('key', function (dist) {
if (!nfs.downloadStream && !nfs.download) {
return next();
}
_downloads[name] = (_downloads[name] || 0) + 1;
if (typeof dist.size === 'number') {
res.setHeader('Content-Length', dist.size);
}
res.setHeader('Content-Type', mime.lookup(dist.key));
res.setHeader('Content-Disposition', 'attachment; filename="' + filename + '"');
res.setHeader('ETag', dist.shasum);
if (nfs.downloadStream) {
nfs.downloadStream(dist.key, res, {timeout: DOWNLOAD_TIMEOUT},
function (err) {
if (err) {
// TODO: just end or send error response?
return next(err);
}
});
return;
}
// use download file api
var tmpPath = path.join(config.uploadDir, utility.randomString() + dist.key);
function cleanup() {
fs.unlink(tmpPath, utility.noop);
}
nfs.download(dist.key, tmpPath, {timeout: DOWNLOAD_TIMEOUT},
function (err) {
if (err) {
cleanup();
return next(err);
}
var tarball = fs.createReadStream(tmpPath);
tarball.on('error', cleanup);
tarball.on('end', cleanup);
tarball.pipe(res);
});
});
};
setInterval(function () {
// save download count
var totals = [];
for (var name in _downloads) {
var count = _downloads[name];
totals.push([name, count]);
}
_downloads = {};
if (totals.length === 0) {
return;
}
debug('save download total: %j', totals);
var date = utility.YYYYMMDD();
var next = function () {
var item = totals.shift();
if (!item) {
// done
return;
}
DownloadTotal.plusTotal({name: item[0], date: date, count: item[1]}, function (err) {
if (!err) {
return next();
}
logger.error(err);
debug('save download %j error: %s', item, err);
totals.push(item);
// save to _downloads
for (var i = 0; i < totals.length; i++) {
var v = totals[i];
var name = v[0];
_downloads[name] = (_downloads[name] || 0) + v[1];
}
// end
});
};
next();
}, 5000);
exports.upload = function (req, res, next) {
var length = Number(req.headers['content-length']) || 0;
if (!length || req.headers['content-type'] !== 'application/octet-stream') {
return next();
}
var username = req.session.name;
var name = req.params.name;
var id = Number(req.params.rev);
var filename = req.params.filename;
var version = filename.substring(name.length + 1);
version = version.replace(/\.tgz$/, '');
// save version on pkg upload
debug('%s: upload %s, file size: %d', username, req.url, length);
Module.getById(id, function (err, mod) {
if (err || !mod) {
return next(err);
}
if (!common.isMaintainer(req, mod.package.maintainers) || mod.name !== name) {
return res.json(403, {
error: 'no_perms',
reason: 'Current user can not publish this module'
});
}
if (mod.version !== 'next') {
// rev wrong
return res.json(403, {
error: 'rev_wrong',
reason: 'rev not match next module'
});
}
var filepath = common.getTarballFilepath(filename);
var ws = fs.createWriteStream(filepath);
var shasum = crypto.createHash('sha1');
req.pipe(ws);
var dataSize = 0;
req.on('data', function (data) {
shasum.update(data);
dataSize += data.length;
});
ws.on('finish', function () {
if (dataSize !== length) {
return res.json(403, {
error: 'size_wrong',
reason: 'Header size ' + length + ' not match download size ' + dataSize,
});
}
shasum = shasum.digest('hex');
var options = {
key: common.getCDNKey(name, filename),
size: length,
shasum: shasum
};
nfs.upload(filepath, options, function (err, result) {
// remove tmp file whatever
fs.unlink(filepath, utility.noop);
if (err) {
return next(err);
}
var dist = {
shasum: shasum,
size: length
};
// if nfs upload return a key, record it
if (result.url) {
dist.tarball = result.url;
} else if (result.key) {
dist.key = result.key;
dist.tarball = result.key;
}
mod.package.dist = dist;
mod.package.version = version;
debug('%s module: save file to %s, size: %d, sha1: %s, dist: %j, version: %s',
id, filepath, length, shasum, dist, version);
Module.update(mod, function (err, result) {
if (err) {
return next(err);
}
res.json(201, {ok: true, rev: String(result.id)});
});
});
});
});
};
exports.updateLatest = function (req, res, next) {
var username = req.session.name;
var name = req.params.name;
var version = semver.valid(req.params.version);
if (!version) {
return res.json(400, {
error: 'Params Invalid',
reason: 'Invalid version: ' + req.params.version,
});
}
Module.get(name, 'next', function (err, nextMod) {
if (err) {
return next(err);
}
if (!nextMod) {
return next();
}
var match = nextMod.package.maintainers.filter(function (item) {
return item.name === username;
});
if (match.length === 0) {
return res.json(401, {
error: 'noperms',
reason: 'Current user can not publish this module'
});
}
// check version if not match pkg upload
if (nextMod.package.version !== version) {
return res.json(403, {
error: 'version_wrong',
reason: 'version not match'
});
}
var body = req.body;
nextMod.version = version;
nextMod.author = username;
body.dist = nextMod.package.dist;
body.maintainers = nextMod.package.maintainers;
if (!body.author) {
body.author = {
name: username,
};
}
body._publish_on_cnpm = true;
nextMod.package = body;
// reset publish time
nextMod.publish_time = Date.now();
debug('update %s:%s %j', nextMod.package.name, nextMod.package.version, nextMod.package.dist);
// change latest to version
Module.update(nextMod, function (err) {
if (err) {
debug('update nextMod %s error: %s', name, err);
return next(err);
}
// set latest tag
Module.addTag(name, 'latest', version, function (err) {
if (err) {
return next(err);
}
// add a new next version
nextMod.version = 'next';
Module.add(nextMod, function (err, result) {
if (err) {
return next(err);
}
res.json(201, {ok: true, rev: String(result.id)});
});
});
});
});
};
exports.addPackageAndDist = function (req, res, next) {
// 'dist-tags': { latest: '0.0.2' },
// _attachments:
// { 'nae-sandbox-0.0.2.tgz':
// { content_type: 'application/octet-stream',
// data: 'H4sIAAAAA
// length: 9883
var pkg = req.body;
var username = req.session.name;
var name = req.params.name;
var filename = Object.keys(pkg._attachments)[0];
var attachment = pkg._attachments[filename];
var version = filename.match(/\-([\.\d\w\_]+?)\.tgz$/);
if (!version) {
return res.json(403, {
error: 'version_error',
reason: filename + ' version not found',
});
}
version = version[1];
var versionPackage = pkg.versions[version];
versionPackage._publish_on_cnpm = true;
var distTags = pkg['dist-tags'] || {};
var tags = []; // tag, version
for (var t in distTags) {
tags.push([t, distTags[t]]);
}
debug('addPackageAndDist %s:%s, attachment size: %s', name, version, attachment.length);
var ep = eventproxy.create();
ep.fail(next);
Module.get(name, version, ep.done('exists'));
var shasum;
ep.on('exists', function (exists) {
if (exists) {
return res.json(409, {
error: 'conflict',
reason: 'Document update conflict.'
});
}
// upload attachment
var tarballBuffer;
try {
tarballBuffer = new Buffer(attachment.data, 'base64');
} catch (e) {
return next(e);
}
if (tarballBuffer.length !== attachment.length) {
return res.json(403, {
error: 'size_wrong',
reason: 'Attachment size ' + attachment.length + ' not match download size ' + tarballBuffer.length,
});
}
shasum = crypto.createHash('sha1');
shasum.update(tarballBuffer);
shasum = shasum.digest('hex');
var options = {
key: common.getCDNKey(name, filename),
shasum: shasum
};
nfs.uploadBuffer(tarballBuffer, options, ep.done('upload'));
});
ep.on('upload', function (result) {
debug('upload %j', result);
var dist = {
shasum: shasum,
size: attachment.length
};
// if nfs upload return a key, record it
if (result.url) {
dist.tarball = result.url;
} else if (result.key) {
dist.key = result.key;
dist.tarball = result.key;
}
var mod = {
name: name,
version: version,
author: username,
package: versionPackage
};
mod.package.dist = dist;
Module.add(mod, ep.done(function (r) {
debug('%s module: save file to %s, size: %d, sha1: %s, dist: %j, version: %s',
r.id, dist.tarball, dist.size, shasum, dist, version);
ep.emit('saveModule', r.id);
}));
});
ep.on('saveModule', function () {
if (tags.length === 0) {
return ep.emit('saveTags');
}
tags.forEach(function (item) {
Module.addTag(name, item[0], item[1], ep.done('saveTag'));
});
ep.after('saveTag', tags.length, function () {
ep.emit('saveTags');
});
});
ep.all('saveModule', 'saveTags', function (moduleId) {
res.json(201, {ok: true, rev: String(moduleId)});
});
};
exports.add = function (req, res, next) {
var username = req.session.name;
var name = req.params.name;
var pkg = req.body || {};
if (!common.isMaintainer(req, pkg.maintainers)) {
return res.json(403, {
error: 'no_perms',
reason: 'Current user can not publish this module'
});
}
if (pkg._attachments && Object.keys(pkg._attachments).length > 0) {
return exports.addPackageAndDist(req, res, next);
}
var ep = eventproxy.create();
ep.fail(next);
Module.getLatest(name, ep.doneLater('latest'));
Module.get(name, 'next', ep.done(function (nextMod) {
if (nextMod) {
nextMod.exists = true;
return ep.emit('next', nextMod);
}
// ensure next module exits
// because updateLatest will create next module fail
nextMod = {
name: name,
version: 'next',
author: username,
package: {
name: name,
version: 'next',
description: pkg.description,
readme: pkg.readme,
maintainers: pkg.maintainers,
},
};
debug('add next module: %s', name);
Module.add(nextMod, ep.done(function (result) {
nextMod.id = result.id;
ep.emit('next', nextMod);
}));
}));
ep.all('latest', 'next', function (latestMod, nextMod) {
var maintainers = latestMod && latestMod.package.maintainers.length > 0 ?
latestMod.package.maintainers : nextMod.package.maintainers;
if (!common.isMaintainer(req, maintainers)) {
return res.json(403, {
error: 'no_perms',
reason: 'Current user can not publish this module'
});
}
debug('add %s rev: %s, version: %s', name, nextMod.id, nextMod.version);
if (latestMod || nextMod.version !== 'next') {
return res.json(409, {
error: 'conflict',
reason: 'Document update conflict.'
});
}
res.json(201, {
ok: true,
id: name,
rev: String(nextMod.id),
});
});
};
exports.removeWithVersions = function (req, res, next) {
debug('removeWithVersions module %s, with info %j', req.params.name, req.body);
var name = req.params.name;
var username = req.session.name;
var versions = req.body.versions || {};
var ep = eventproxy.create();
ep.fail(next);
// step1: list all the versions
Module.listByName(name, ep.doneLater('list'));
ep.once('list', function (mods) {
if (!mods || !mods.length) {
return next();
}
// step2: check permission
var firstMod = mods[0];
if (!common.isMaintainer(req, firstMod.package.maintainers) || firstMod.name !== name) {
return res.json(403, {
error: 'no_perms',
reason: 'Current user can not update this module'
});
}
// step3: calculate which versions need to remove and
// which versions need to remain
var removeVersions = [];
var removeVersionMaps = {};
var remainVersions = [];
for (var i = 0; i < mods.length; i++) {
var v = mods[i].version;
if (v === 'next') {
continue;
}
if (!versions[v]) {
removeVersions.push(v);
removeVersionMaps[v] = true;
} else {
remainVersions.push(v);
}
}
if (!removeVersions.length) {
debug('no versions need to remove');
return res.json(201, { ok: true });
}
debug('remove versions: %j, remain versions: %j', removeVersions, remainVersions);
// step 4: remove all the versions which need to remove
Module.removeByNameAndVersions(name, removeVersions, ep.done('removeModules'));
// step5: list all the tags
Module.listTags(name, ep.done(function (tags) {
var removeTags = [];
var latestRemoved = false;
tags.forEach(function (tag) {
// this tag need be removed
if (removeVersionMaps[tag.version]) {
removeTags.push(tag.id);
if (tag.tag === 'latest') {
latestRemoved = true;
}
}
});
if (!removeTags.length) {
debug('no tag need be remove');
ep.emit('removeTags');
ep.emit('latest');
return;
}
debug('remove tags: %j', removeTags);
// step 6: remove all the tags which need to remove
Module.removeTagsByIds(removeTags, ep.done('removeTags'));
// step 7: check if latest tag being removed.
// need generate a new latest tag
if (!latestRemoved || !remainVersions[0]) {
ep.emit('latest');
} else {
debug('latest tags removed, generate a new latest tag with new version: %s',
remainVersions[0]);
ep.emit('newLatestVersion', remainVersions[0]);
}
}));
});
ep.all('newLatestVersion', 'removeTags', function (version) {
Module.addTag(name, 'latest', version, ep.done('latest'));
});
ep.all('removeModules', 'removeTags', 'latest', function () {
return res.json(201, { ok: true });
});
};
exports.removeTar = function (req, res, next) {
debug('remove tarball with filename: %s, id: %s', req.params.filename, req.params.rev);
var id = Number(req.params.rev);
var filename = req.params.filename;
var name = req.params.name;
var username = req.session.name;
var ep = eventproxy.create();
ep.fail(next);
Module.getById(id, ep.doneLater('get'));
ep.once('get', function (mod) {
if (!mod) {
return next();
}
if (!common.isMaintainer(req, mod.package.maintainers) || mod.name !== name) {
return res.json(403, {
error: 'no_perms',
reason: 'Current user can not delete this tarball'
});
}
var key = mod.package.dist && mod.package.dist.key;
key = key || common.getCDNKey(mod.name, filename);
nfs.remove(key, ep.done(function () {
res.json(200, {ok: true});
}));
});
};
exports.removeAll = function (req, res, next) {
debug('remove all the module with name: %s, id: %s', req.params.name, req.params.rev);
var id = Number(req.params.rev);
var name = req.params.name;
var username = req.session.name;
var ep = eventproxy.create();
ep.fail(next);
Module.listByName(name, ep.doneLater('list'));
ep.once('list', function (mods) {
debug('removeAll module %s: %d', name, mods.length);
var mod = mods[0];
if (!mod) {
return next();
}
if (!common.isMaintainer(req, mod.package.maintainers) || mod.name !== name) {
return res.json(403, {
error: 'no_perms',
reason: 'Current user can not delete this tarball'
});
}
Total.plusDeleteModule(utility.noop);
Module.removeByName(name, ep.done('remove'));
Module.removeTags(name, ep.done('removeTags'));
});
ep.all('list', 'remove', 'removeTags', function (mods) {
var keys = [];
for (var i = 0; i < mods.length; i++) {
var key = urlparse(mods[i].dist_tarball).path;
key && keys.push(key);
}
var queue = new Bagpipe(5);
keys.forEach(function (key) {
queue.push(nfs.remove.bind(nfs), key, function () {
//ignore err here
ep.emit('removeTar');
});
});
ep.after('removeTar', keys.length, function () {
res.json(200, {ok: true});
});
});
};
function parseModsForList(updated, mods, req) {
var results = {
_updated: updated
};
for (var i = 0; i < mods.length; i++) {
var mod = mods[i];
var pkg = {};
try {
pkg = JSON.parse(mod.package);
} catch (e) {
//ignore this pkg
continue;
}
pkg['dist-tags'] = {
latest: pkg.version
};
common.setDownloadURL(pkg, req);
results[mod.name] = pkg;
}
return results;
}
exports.listAllModules = function (req, res, next) {
var updated = Date.now();
Module.listSince(0, function (err, mods) {
if (err) {
return next(err);
}
return res.json(parseModsForList(updated, mods, req));
});
};
exports.listAllModulesSince = function (req, res, next) {
var query = req.query || {};
if (query.stale !== 'update_after') {
return res.json(400, {
error: 'query_parse_error',
reason: 'Invalid value for `stale`.'
});
}
debug('list all modules from %s', req.startkey);
var startkey = Number(query.startkey) || 0;
var updated = Date.now();
Module.listSince(startkey, function (err, mods) {
if (err) {
return next(err);
}
res.json(parseModsForList(updated, mods, req));
});
};
exports.listAllModuleNames = function (req, res, next) {
Module.listShort(function (err, mods) {
if (err) {
return next(err);
}
var results = mods.map(function (m) {
return m.name;
});
res.json(results);
});
};

View File

@@ -0,0 +1,60 @@
/**!
* cnpmjs.org - controllers/registry/package/deprecate.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var packageService = require('../../../services/package');
module.exports = deprecateVersions;
/**
* @see https://github.com/cnpm/cnpmjs.org/issues/415
*/
function* deprecateVersions() {
var body = this.request.body;
var name = this.params.name || this.params[0];
var tasks = [];
for (var version in body.versions) {
tasks.push(packageService.getModule(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(packageService.updateModulePackage(row.id, row.package));
}
}
yield updateTasks;
// update last modified
yield* packageService.updateModuleLastModified(name);
this.status = 201;
this.body = {
ok: true
};
}

View File

@@ -0,0 +1,109 @@
/**!
* cnpmjs.org - controllers/registry/package/download.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:controllers:registry:download');
var mime = require('mime');
var utility = require('utility');
var defer = require('co-defer');
var nfs = require('../../../common/nfs');
var logger = require('../../../common/logger');
var common = require('../../../lib/common');
var downloadAsReadStream = require('../../utils').downloadAsReadStream;
var packageService = require('../../../services/package');
var downloadTotalService = require('../../../services/download_total');
var _downloads = {};
module.exports = function* download(next) {
var name = this.params.name || this.params[0];
var filename = this.params.filename || this.params[1];
var version = filename.slice(name.length + 1, -4);
var row = yield* packageService.getModule(name, version);
// can not get dist
var url = null;
if (typeof nfs.url === 'function') {
url = nfs.url(common.getCDNKey(name, filename));
}
debug('download %s %s %s %s', name, filename, version, url);
if (!row || !row.package || !row.package.dist) {
if (!url) {
return yield* next;
}
this.status = 302;
this.set('Location', url);
_downloads[name] = (_downloads[name] || 0) + 1;
return;
}
var dist = row.package.dist;
if (!dist.key) {
debug('get tarball by 302, url: %s', dist.tarball || url);
this.status = 302;
this.set('Location', dist.tarball || url);
_downloads[name] = (_downloads[name] || 0) + 1;
return;
}
// else use `dist.key` to get tarball from nfs
if (!nfs.download) {
return yield* next;
}
_downloads[name] = (_downloads[name] || 0) + 1;
if (typeof dist.size === 'number' && dist.size > 0) {
this.length = dist.size;
}
this.type = mime.lookup(dist.key);
this.attachment(filename);
this.etag = dist.shasum;
this.body = yield* downloadAsReadStream(dist.key);
};
defer.setInterval(function* () {
// save download count
var totals = [];
for (var name in _downloads) {
var count = _downloads[name];
totals.push([name, count]);
}
_downloads = {};
if (totals.length === 0) {
return;
}
debug('save download total: %j', totals);
var date = utility.YYYYMMDD();
for (var i = 0; i < totals.length; i++) {
var item = totals[i];
var name = item[0];
var count = item[1];
try {
yield* downloadTotalService.plusModuleTotal({ name: name, date: date, count: count });
} catch (err) {
err.message += '; name: ' + name + ', count: ' + count + ', date: ' + date;
logger.error(err);
// save back to _downloads, try again next time
_downloads[name] = (_downloads[name] || 0) + count;
}
}
}, 5000);

View File

@@ -0,0 +1,217 @@
/**!
* cnpmjs.org - controllers/registry/package/list.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:controllers:registry:package:list');
var packageService = require('../../../services/package');
var npmService = require('../../../services/npm');
var config = require('../../../config');
var setDownloadURL = require('../../../lib/common').setDownloadURL;
var SyncModuleWorker = require('../../sync_module_worker');
/**
* list all version of a module
* GET /:name
*/
module.exports = function* list() {
var orginalName = this.params.name || this.params[0];
var name = orginalName;
var rs = yield [
packageService.getModuleLastModified(name),
packageService.listModuleTags(name)
];
var modifiedTime = rs[0];
var tags = rs[1];
debug('show %s(%s), last modified: %s, tags: %j', name, orginalName, modifiedTime, tags);
if (modifiedTime) {
// find out the latest modfied time
// because update tags only modfied tag, wont change module gmt_modified
for (var i = 0; i < tags.length; i++) {
var tag = tags[i];
if (tag.gmt_modified > modifiedTime) {
modifiedTime = tag.gmt_modified;
}
}
// use modifiedTime as etag
this.set('ETag', '"' + modifiedTime.getTime() + '"');
// must set status first
this.status = 200;
if (this.fresh) {
debug('%s not change at %s, 304 return', name, modifiedTime);
this.status = 304;
return;
}
}
var r = yield [
packageService.listModulesByName(name),
packageService.listStarUserNames(name),
packageService.listMaintainers(name),
];
var rows = r[0];
var starUsers = r[1];
var maintainers = r[2];
debug('show %s got %d rows, %d tags, %d star users, maintainers: %j',
name, rows.length, tags.length, starUsers.length, maintainers);
var starUserMap = {};
for (var i = 0; i < starUsers.length; i++) {
starUserMap[starUsers[i]] = true;
}
starUsers = starUserMap;
if (rows.length === 0) {
// check if unpublished
var unpublishedInfo = yield* packageService.getUnpublishedModule(name);
debug('show unpublished %j', unpublishedInfo);
if (unpublishedInfo) {
this.status = 404;
this.body = {
_id: orginalName,
name: orginalName,
time: {
modified: unpublishedInfo.package.time,
unpublished: unpublishedInfo.package,
},
_attachments: {}
};
return;
}
}
// if module not exist in this registry,
// sync the module backend and return package info from official registry
if (rows.length === 0) {
if (!this.allowSync) {
this.status = 404;
this.body = {
error: 'not_found',
reason: 'document not found'
};
return;
}
// start sync
var logId = yield* SyncModuleWorker.sync(name, 'sync-by-install');
debug('start sync %s, get log id %s', name, logId);
// try to get package from official registry
var r = yield* npmService.request('/' + name, {
registry: config.officialNpmRegistry
});
debug('requet from officialNpmRegistry response %s', r.status);
if (r.status !== 200) {
this.status = 404;
this.body = {
error: 'not_found',
reason: 'document not found'
};
return;
}
this.body = r.data;
return;
}
var latestMod = null;
var readme = null;
// set tags
var distTags = {};
for (var i = 0; i < tags.length; i++) {
var t = tags[i];
distTags[t.tag] = t.version;
}
// set versions and times
var versions = {};
var times = {};
var attachments = {};
var createdTime = null;
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
var pkg = row.package;
setDownloadURL(pkg, this);
pkg._cnpm_publish_time = row.publish_time;
versions[pkg.version] = pkg;
var t = times[pkg.version] = row.publish_time ? new Date(row.publish_time) : row.gmt_modified;
if ((!distTags.latest && !latestMod) || distTags.latest === pkg.version) {
latestMod = row;
readme = pkg.readme;
}
delete pkg.readme;
if (maintainers.length > 0) {
pkg.maintainers = maintainers;
}
if (!createdTime || t < createdTime) {
createdTime = t;
}
}
if (modifiedTime && createdTime) {
var ts = {
modified: modifiedTime,
created: createdTime,
};
for (var t in times) {
ts[t] = times[t];
}
times = ts;
}
if (!latestMod) {
latestMod = rows[0];
}
var rev = String(latestMod.id);
var pkg = latestMod.package;
if (tags.length === 0) {
// some sync error reason, will cause tags missing
// set latest tag at least
distTags.latest = pkg.version;
}
var info = {
_id: orginalName,
_rev: rev,
name: orginalName,
description: pkg.description,
"dist-tags": distTags,
maintainers: pkg.maintainers,
time: times,
users: starUsers,
author: pkg.author,
repository: pkg.repository,
versions: versions,
readme: readme,
_attachments: attachments,
};
info.readmeFilename = pkg.readmeFilename;
info.homepage = pkg.homepage;
info.bugs = pkg.bugs;
info.license = pkg.license;
debug('show module %s: %s, latest: %s', orginalName, rev, latestMod.version);
this.body = info;
};

View File

@@ -0,0 +1,30 @@
/**!
* cnpmjs.org - controllers/registry/package/list_all.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var packageService = require('../../../services/package');
// GET /-/all
// List all packages names
// https://github.com/npm/npm-registry-client/blob/master/lib/get.js#L86
module.exports = function* () {
var updated = Date.now();
var names = yield* packageService.listAllPublicModuleNames();
var result = { _updated: updated };
names.forEach(function (name) {
result[name] = true;
});
this.body = result;
};

View File

@@ -0,0 +1,59 @@
/**!
* cnpmjs.org - controllers/registry/package/list_since.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var packageService = require('../../../services/package');
var A_WEEK_MS = 3600000 * 24 * 7;
// GET /-/all/since?stale=update_after&startkey={key}
// List packages names since startkey
// https://github.com/npm/npm-registry-client/blob/master/lib/get.js#L89
module.exports = function* () {
var query = this.query;
if (query.stale !== 'update_after') {
this.status = 400;
this.body = {
error: 'query_parse_error',
reason: 'Invalid value for `stale`.'
};
return;
}
var startkey = Number(query.startkey);
if (!startkey) {
this.status = 400;
this.body = {
error: 'query_parse_error',
reason: 'Invalid value for `startkey`.'
};
return;
}
var updated = Date.now();
if (updated - startkey > A_WEEK_MS) {
startkey = updated - A_WEEK_MS;
console.warn('[%s] list modules since time out of range: query: %j, ip: %s',
Date(), query, this.ip);
}
var names = yield* packageService.listPublicModuleNamesSince(startkey);
var result = { _updated: updated };
names.forEach(function (name) {
result[name] = true;
});
this.body = result;
};

View File

@@ -0,0 +1,67 @@
/**!
* cnpmjs.org - controllers/registry/package/remove.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:controllers:registry:package:remove');
var urlparse = require('url').parse;
var packageService = require('../../../services/package');
var totalService = require('../../../services/total');
var nfs = require('../../../common/nfs');
var logger = require('../../../common/logger');
// DELETE /:name/-rev/:rev
// https://github.com/npm/npm-registry-client/blob/master/lib/unpublish.js#L25
module.exports = function* remove(next) {
var name = this.params.name || this.params[0];
var rev = this.params.rev || this.params[1];
debug('remove all the module with name: %s, id: %s', name, rev);
var mods = yield* packageService.listModulesByName(name);
debug('removeAll module %s: %d', name, mods.length);
var mod = mods[0];
if (!mod) {
return yield* next;
}
yield [
packageService.removeModulesByName(name),
packageService.removeModuleTags(name),
totalService.plusDeleteModule(),
];
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);
}
try {
yield keys.map(function (key) {
return nfs.remove(key);
});
} catch (err) {
logger.error(err);
}
// remove the maintainers
yield* packageService.removeAllMaintainers(name);
this.body = { ok: true };
};

View File

@@ -0,0 +1,74 @@
/**!
* cnpmjs.org - controllers/registry/package/remove_version.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:controllers:registry:package:remove_version');
var packageService = require('../../../services/package');
var nfs = require('../../../common/nfs');
var logger = require('../../../common/logger');
var getCDNKey = require('../../../lib/common').getCDNKey;
// DELETE /:name/download/:filename/-rev/:rev
// https://github.com/npm/npm-registry-client/blob/master/lib/unpublish.js#L97
module.exports = function* removeOneVersion(next) {
var name = this.params.name || this.params[0];
var filename = this.params.filename || this.params[1];
var id = Number(this.params.rev || this.params[2]);
// cnpmjs.org-2.0.0.tgz
var version = filename.split(name + '-')[1];
if (version) {
// 2.0.0.tgz
version = version.substring(0, version.lastIndexOf('.tgz'));
}
if (!version) {
return yield* next;
}
debug('remove tarball with filename: %s, version: %s, revert to => rev id: %s', filename, version, id);
if (isNaN(id)) {
return yield* next;
}
var rs = yield [
packageService.getModuleById(id),
packageService.getModule(name, version),
];
var revertTo = rs[0];
var mod = rs[1]; // module need to delete
if (!mod || mod.name !== name) {
return yield* next;
}
var key = mod.package && mod.package.dist && mod.package.dist.key;
if (!key) {
key = getCDNKey(mod.name, filename);
}
if (revertTo && revertTo.package) {
debug('removing key: %s from nfs, revert to %s@%s', key, revertTo.name, revertTo.package.version);
} else {
debug('removing key: %s from nfs, no revert mod', key);
}
try {
yield nfs.remove(key);
} catch (err) {
logger.error(err);
}
// remove version from table
yield* packageService.removeModulesByNameAndVersions(name, [version]);
debug('removed %s@%s', name, version);
this.body = { ok: true };
};

View File

@@ -0,0 +1,237 @@
/**!
* cnpmjs.org - controllers/registry/package/save.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:controllers:registry:package:save');
var crypto = require('crypto');
var deprecateVersions = require('./deprecate');
var packageService = require('../../../services/package');
var common = require('../../../lib/common');
var nfs = require('../../../common/nfs');
var config = require('../../../config');
// old flows:
// 1. add()
// 2. upload()
// 3. updateLatest()
//
// new flows: only one request
// PUT /:name
// https://github.com/npm/npm-registry-client/blob/master/lib/publish.js#L84
module.exports = function* save(next) {
// 'dist-tags': { latest: '0.0.2' },
// _attachments:
// { 'nae-sandbox-0.0.2.tgz':
// { content_type: 'application/octet-stream',
// data: 'H4sIAAAAA
// length: 9883
var pkg = this.request.body;
var username = this.user.name;
var name = this.params.name || this.params[0];
var filename = Object.keys(pkg._attachments || {})[0];
var version = Object.keys(pkg.versions || {})[0];
if (!version) {
this.status = 400;
this.body = {
error: 'version_error',
reason: 'package.versions is empty'
};
return;
}
// check maintainers
var result = yield* packageService.authMaintainer(name, username);
if (!result.isMaintainer) {
this.status = 403;
this.body = {
error: 'forbidden user',
reason: username + ' not authorized to modify ' + name +
', please contact maintainers: ' + result.maintainers.join(', ')
};
return;
}
if (!filename) {
var hasDeprecated = false;
for (var v in pkg.versions) {
var row = pkg.versions[v];
if (typeof row.deprecated === 'string') {
hasDeprecated = true;
break;
}
}
if (hasDeprecated) {
return yield* deprecateVersions.call(this, next);
}
this.status = 400;
this.body = {
error: 'attachment_error',
reason: 'package._attachments is empty'
};
return;
}
var attachment = pkg._attachments[filename];
var versionPackage = pkg.versions[version];
var maintainers = versionPackage.maintainers;
// should never happened in normal request
if (!maintainers) {
this.status = 400;
this.body = {
error: 'maintainers error',
reason: 'request body need maintainers'
};
return;
}
// notice that admins can not publish to all modules
// (but admins can add self to maintainers first)
// make sure user in auth is in maintainers
// should never happened in normal request
var m = maintainers.filter(function (maintainer) {
return maintainer.name === username;
});
if (m.length === 0) {
this.status = 403;
this.body = {
error: 'maintainers error',
reason: username + ' does not in maintainer list'
};
return;
}
// TODO: add this info into some table
versionPackage._publish_on_cnpm = true;
var distTags = pkg['dist-tags'] || {};
var tags = []; // tag, version
for (var t in distTags) {
tags.push([t, distTags[t]]);
}
if (tags.length === 0) {
this.status = 400;
this.body = {
error: 'invalid',
reason: 'dist-tags should not be empty'
};
return;
}
debug('%s publish new %s:%s, attachment size: %s, maintainers: %j, distTags: %j',
username, name, version, attachment.length, versionPackage.maintainers, distTags);
var exists = yield* packageService.getModule(name, version);
var shasum;
if (exists) {
this.status = 403;
this.body = {
error: 'forbidden',
reason: 'cannot modify pre-existing version: ' + version
};
return;
}
// upload attachment
var tarballBuffer;
tarballBuffer = new Buffer(attachment.data, 'base64');
if (tarballBuffer.length !== attachment.length) {
this.status = 403;
this.body = {
error: 'size_wrong',
reason: 'Attachment size ' + attachment.length
+ ' not match download size ' + tarballBuffer.length,
};
return;
}
if (!distTags.latest) {
// need to check if latest tag exists or not
var latest = yield* packageService.getModuleByTag(name, 'latest');
if (!latest) {
// auto add latest
tags.push(['latest', tags[0][1]]);
debug('auto add latest tag: %j', tags);
}
}
shasum = crypto.createHash('sha1');
shasum.update(tarballBuffer);
shasum = shasum.digest('hex');
var options = {
key: common.getCDNKey(name, filename),
shasum: shasum
};
var uploadResult = yield nfs.uploadBuffer(tarballBuffer, options);
debug('upload %j', uploadResult);
var dist = {
shasum: shasum,
size: attachment.length
};
// if nfs upload return a key, record it
if (uploadResult.url) {
dist.tarball = uploadResult.url;
} else if (uploadResult.key) {
dist.key = uploadResult.key;
dist.tarball = uploadResult.key;
}
var mod = {
name: name,
version: version,
author: username,
package: versionPackage
};
mod.package.dist = dist;
yield* addDepsRelations(mod.package);
var addResult = yield* packageService.saveModule(mod);
debug('%s module: save file to %s, size: %d, sha1: %s, dist: %j, version: %s',
addResult.id, dist.tarball, dist.size, shasum, dist, version);
if (tags.length) {
yield tags.map(function (tag) {
// tag: [tagName, version]
return packageService.addModuleTag(name, tag[0], tag[1]);
});
}
// ensure maintainers exists
var maintainerNames = maintainers.map(function (item) {
return item.name;
});
yield* packageService.addMaintainers(name, maintainerNames);
this.status = 201;
this.body = {
ok: true,
rev: String(addResult.id)
};
};
function* addDepsRelations(pkg) {
var dependencies = Object.keys(pkg.dependencies || {});
if (dependencies.length > config.maxDependencies) {
dependencies = dependencies.slice(0, config.maxDependencies);
}
yield packageService.addDependencies(pkg.name, dependencies);
}

View File

@@ -0,0 +1,84 @@
/**!
* cnpmjs.js - controllers/registry/package/show.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:controllers:registry:package:show');
var semver = require('semver');
var packageService = require('../../../services/package');
var npmService = require('../../../services/npm');
var setDownloadURL = require('../../../lib/common').setDownloadURL;
var SyncModuleWorker = require('../../sync_module_worker');
var config = require('../../../config');
/**
* [deprecate] api
*
* get the special version or tag of a module
*
* GET /:name/:version
* GET /:name/:tag
*/
module.exports = function* show() {
var name = this.params.name || this.params[0];
var tag = this.params.version || this.params[1];
var version = semver.valid(tag);
var mod;
if (version) {
mod = yield* packageService.getModule(name, version);
} else {
mod = yield* packageService.getModuleByTag(name, tag);
}
if (mod) {
setDownloadURL(mod.package, this);
mod.package._cnpm_publish_time = mod.publish_time;
var maintainers = yield* packageService.listMaintainers(name);
if (maintainers.length > 0) {
mod.package.maintainers = maintainers;
}
this.body = mod.package;
return;
}
// if not fond, sync from source registry
if (!this.allowSync) {
this.status = 404;
this.body = {
error: 'not exist',
reason: 'version not found: ' + version
};
return;
}
// start sync
var logId = yield* SyncModuleWorker.sync(name, 'sync-by-install');
debug('start sync %s, get log id %s', name, logId);
// rty to get package from official registry
var r = yield npmService.request('/' + name + '/' + tag, {
registry: config.officialNpmRegistry
});
if (r.status !== 200) {
debug('requet from officialNpmRegistry response %s', r.status);
this.status = 404;
this.body = {
error: 'not exist',
reason: 'tag or version not found: ' + tag
};
return;
}
this.body = r.data;
};

View File

@@ -0,0 +1,69 @@
/**!
* cnpmjs.org - controllers/registry/package/tag.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:controllers:registry:package:tag');
var semver = require('semver');
var util = require('util');
var packageService = require('../../../services/package');
// PUT /:name/:tag
// https://github.com/npm/npm-registry-client/blob/master/lib/tag.js#L4
// this.request("PUT", uri+"/"+tagName, { body : JSON.stringify(version) }, cb)
module.exports = function* tag() {
var version = this.request.body;
var name = this.params.name || this.params[0];
var tag = this.params.tag || this.params[1];
debug('tag %j to %s/%s', version, name, tag);
if (!version) {
this.status = 400;
this.body = {
error: 'version_missed',
reason: 'version not found'
};
return;
}
if (!semver.valid(version)) {
this.status = 403;
var reason = util.format('setting tag %s to invalid version: %s: %s/%s',
tag, version, name, tag);
this.body = {
error: 'forbidden',
reason: reason
};
return;
}
var mod = yield* packageService.getModule(name, version);
if (!mod) {
this.status = 403;
var reason = util.format('setting tag %s to unknown version: %s: %s/%s',
tag, version, name, tag);
this.body = {
error: 'forbidden',
reason: reason
};
return;
}
var row = yield* packageService.addModuleTag(name, tag, version);
this.status = 201;
this.body = {
ok: true,
modified: row.gmt_modified
};
};

View File

@@ -0,0 +1,180 @@
/**!
* cnpmjs.org - controllers/registry/package/update.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:controllers:registry:package:update');
var packageService = require('../../../services/package');
var userService = require('../../../services/user');
var config = require('../../../config');
// PUT /:name/-rev/:rev
//
// * remove with versions, then will `DELETE /:name/download/:filename/-rev/:rev`
// * ...
module.exports = function* update(next) {
var name = this.params.name || this.params[0];
debug('update module %s, %s, %j', this.url, name, this.request.body);
var body = this.request.body;
if (body.versions) {
yield* updateVersions.call(this, next);
} else if (body.maintainers) {
yield* updateMaintainers.call(this, next);
} else {
yield* next;
}
};
// update with versions
// https://github.com/npm/npm-registry-client/blob/master/lib/unpublish.js#L63
function* updateVersions(next) {
var name = this.params.name || this.params[0];
// left versions
var versions = this.request.body.versions;
// step1: list all the versions
var mods = yield* packageService.listModulesByName(name);
debug('removeWithVersions module %s, left versions %j, %s mods',
name, Object.keys(versions), mods && mods.length);
if (!mods || !mods.length) {
return yield* next;
}
// step3: calculate which versions need to remove and
// which versions need to remain
var removeVersions = [];
var removeVersionMaps = {};
var remainVersions = [];
for (var i = 0; i < mods.length; i++) {
var v = mods[i].version;
if (!versions[v]) {
removeVersions.push(v);
removeVersionMaps[v] = true;
} else {
remainVersions.push(v);
}
}
if (!removeVersions.length) {
debug('no versions need to remove');
this.status = 201;
this.body = { ok: true };
return;
}
debug('remove versions: %j, remain versions: %j', removeVersions, remainVersions);
// step 4: remove all the versions which need to remove
// let removeTar do remove versions from module table
var tags = yield* packageService.listModuleTags(name);
var removeTags = [];
var latestRemoved = false;
tags.forEach(function (tag) {
// this tag need be removed
if (removeVersionMaps[tag.version]) {
removeTags.push(tag.id);
if (tag.tag === 'latest') {
latestRemoved = true;
}
}
});
debug('remove tags: %j', removeTags);
if (removeTags.length) {
// step 5: remove all the tags
yield* packageService.removeModuleTagsByIds(removeTags);
if (latestRemoved && remainVersions[0]) {
debug('latest tags removed, generate a new latest tag with new version: %s',
remainVersions[0]);
// step 6: insert new latest tag
yield* packageService.addModuleTag(name, 'latest', remainVersions[0]);
}
}
// step 7: update last modified, make sure etag change
yield* packageService.updateModuleLastModified(name);
this.status = 201;
this.body = { ok: true };
}
function* updateMaintainers() {
var name = this.params.name || this.params[0];
var body = this.request.body;
debug('updateMaintainers module %s, %j', name, body);
var usernames = body.maintainers.map(function (user) {
return user.name;
});
if (usernames.length === 0) {
this.status = 403;
this.body = {
error: 'invalid operation',
reason: 'Can not remove all maintainers'
};
return;
}
if (config.customUserService) {
// ensure new authors are vaild
var maintainers = yield* packageService.listMaintainerNamesOnly(name);
var map = {};
var newNames = [];
for (var i = 0; i < maintainers.length; i++) {
map[maintainers[i]] = 1;
}
for (var i = 0; i < usernames.length; i++) {
var username = usernames[i];
if (map[username] !== 1) {
newNames.push(username);
}
}
if (newNames.length > 0) {
var users = yield* userService.list(newNames);
var map = {};
for (var i = 0; i < users.length; i++) {
var user = users[i];
map[user.login] = 1;
}
var invailds = [];
for (var i = 0; i < newNames.length; i++) {
var username = newNames[i];
if (map[username] !== 1) {
invailds.push(username);
}
}
if (invailds.length > 0) {
this.status = 403;
this.body = {
error: 'invalid user name',
reason: 'User: `' + invailds.join(', ') + '` not exists'
};
return;
}
}
}
var r = yield* packageService.updateMaintainers(name, usernames);
debug('result: %j', r);
this.status = 201;
this.body = {
ok: true,
id: name,
rev: this.params.rev || this.params[1],
};
}

View File

@@ -1,162 +0,0 @@
/*!
* cnpmjs.org - controllers/registry/user.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:controllers:registry');
var logger = require('../../common/logger');
var User = require('../../proxy/user');
var eventproxy = require('eventproxy');
exports.show = function (req, res, next) {
var name = req.params.name;
User.get(name, function (err, row) {
if (err) {
return next(err);
}
if (!row) {
return next();
}
res.setHeader('etag', '"' + row.rev + '"');
var data = {
_id: 'org.couchdb.user:' + row.name,
_rev: row.rev,
name: row.name,
email: row.email,
type: 'user',
roles: [],
date: row.gmt_modified,
};
res.json(data);
});
};
// json:
// { name: 'fengmk2',
// salt: 'xxxx',
// password_sha: 'xxxxxx',
// email: 'fengmk2@gmail.com',
// _id: 'org.couchdb.user:fengmk2',
// type: 'user',
// roles: [],
// date: '2013-12-04T12:56:13.714Z' } }
exports.add = function (req, res, next) {
var name = req.params.name;
var body = req.body || {};
var user = {
name: body.name,
salt: body.salt,
password_sha: body.password_sha,
email: body.email,
ip: req.socket && req.socket.remoteAddress || '0.0.0.0',
// roles: body.roles || [],
};
if (!user.name || !user.salt || !user.password_sha || !user.email) {
return res.json(422, {
error: 'paramError',
reason: 'params missing'
});
}
debug('add user: %j', user);
var ep = eventproxy.create();
ep.fail(next);
User.get(name, ep.doneLater(function (row) {
if (row) {
return res.json(409, {
error: 'conflict',
reason: 'Document update conflict.'
});
}
User.add(user, ep.done('add'));
}));
ep.once('add', function (result) {
res.setHeader('etag', '"' + result.rev + '"');
// location: 'http://registry.npmjs.org/_users/org.couchdb.user:cnpmjstest1',
res.json(201, {
ok: true,
id: 'org.couchdb.user:' + name,
rev: result.rev
});
});
};
exports.authSession = function (req, res, next) {
// body: {"name":"foo","password":"****"}
var body = req.body || {};
var name = body.name;
var password = body.password;
User.auth(name, password, function (err, user) {
debug('authSession %s: %j', name, user);
if (err) {
return next(err);
}
if (!user) {
return res.json(401, {ok: false, name: null, roles: []});
}
req.session.name = user.name;
res.json(200, {ok: true, name: user.name, roles: []});
});
};
exports.update = function (req, res, next) {
var name = req.params.name;
var rev = req.params.rev;
if (!name || !rev) {
return next();
}
debug('update: %s, rev: %s, session.name: %s', name, rev, req.session.name);
if (name !== req.session.name) {
// must authSession first
res.statusCode = 401;
return res.json({
error: 'unauthorized',
reason: 'Name is incorrect.'
});
}
var body = req.body || {};
var user = {
name: body.name,
salt: body.salt,
password_sha: body.password_sha,
email: body.email,
ip: req.socket && req.socket.remoteAddress || '0.0.0.0',
rev: body.rev || body._rev,
// roles: body.roles || [],
};
User.update(user, function (err, result) {
if (err) {
return next(err);
}
//check rev error
if (!result) {
return res.json(409, {
error: 'conflict',
reason: 'Document update conflict.'
});
}
res.json(201, {
ok: true,
id: 'org.couchdb.user:' + user.name,
rev: result.rev
});
});
};

View File

@@ -0,0 +1,128 @@
/**!
* cnpmjs.org - controllers/registry/user/add.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var ensurePasswordSalt = require('./common').ensurePasswordSalt;
var userService = require('../../../services/user');
var config = require('../../../config');
// npm 1.4.4
// add new user first
// @see https://github.com/npm/npm-registry-client/commit/effb4bc88d443f764f2c2e8b4dd583cc72cf6084
// PUT /-/user/org.couchdb.user:mk2 { accept: 'application/json',
// 'accept-encoding': 'gzip',
// 'user-agent': 'node/v0.11.12 darwin x64',
// host: '127.0.0.1:7001',
// 'content-type': 'application/json',
// 'content-length': '150',
// connection: 'close' } { name: 'mk2',
// password: '123456',
// email: 'fengmk2@gmail.com',
// _id: 'org.couchdb.user:mk2',
// type: 'user',
// roles: [],
// date: '2014-03-15T02:33:19.465Z' }
// old npm flow
// json:
// PUT /-/user/org.couchdb.user:mk2 { accept: 'application/json',
// 'user-agent': 'node/v0.8.26 darwin x64',
// host: '127.0.0.1:7001',
// 'content-type': 'application/json',
// 'content-length': '258',
// connection: 'keep-alive' }
// { name: 'mk2',
// salt: '12351936478446a5466d4fb1633b80f3838b4caaa03649a885ac722cd6',
// password_sha: '123408912a6db1d96b132a90856d99db029cef3d',
// email: 'fengmk2@gmail.com',
// _id: 'org.couchdb.user:mk2',
// type: 'user',
// roles: [],
// date: '2014-03-15T02:39:25.696Z' }
module.exports = function* addUser() {
var name = this.params.name;
var body = this.request.body || {};
var user = {
name: body.name,
// salt: body.salt,
// password_sha: body.password_sha,
email: body.email,
ip: this.ip || '0.0.0.0',
// roles: body.roles || [],
};
ensurePasswordSalt(user, body);
if (!body.password || !user.name || !user.salt || !user.password_sha || !user.email) {
this.status = 422;
this.body = {
error: 'paramError',
reason: 'params missing, name, email or password missing.'
};
return;
}
var loginedUser;
try {
loginedUser = yield* userService.authAndSave(body.name, body.password);
} catch (err) {
this.status = err.status || 500;
this.body = {
error: err.name,
reason: err.message
};
return;
}
if (loginedUser) {
this.status = 201;
this.body = {
ok: true,
id: 'org.couchdb.user:' + loginedUser.login,
rev: Date.now() + '-' + loginedUser.login
};
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* userService.get(name);
if (existUser) {
this.status = 409;
this.body = {
error: 'conflict',
reason: 'User ' + name + ' already exists.'
};
return;
}
// add new user
var result = yield* userService.add(user);
this.etag = '"' + result.rev + '"';
this.status = 201;
this.body = {
ok: true,
id: 'org.couchdb.user:' + name,
rev: result.rev
};
};

View File

@@ -0,0 +1,26 @@
/**!
* cnpmjs.org - controllers/registry/user/common.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var crypto = require('crypto');
var utility = require('utility');
exports.ensurePasswordSalt = function (user, body) {
if (!user.password_sha && body.password) {
// create password_sha on server
user.salt = crypto.randomBytes(30).toString('hex');
user.password_sha = utility.sha1(body.password + user.salt);
}
};

View File

@@ -0,0 +1,69 @@
/**!
* cnpmjs.org - controllers/registry/user/show.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var userService = require('../../../services/user');
// GET /-/user/org.couchdb.user::name
module.exports = function* show(next) {
var name = this.params.name;
var user = yield* userService.getAndSave(name);
if (!user) {
return yield* next;
}
var data = user.json;
if (!data) {
data = {
_id: 'org.couchdb.user:' + user.name,
_rev: user.rev,
name: user.name,
email: user.email,
type: 'user',
roles: [],
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,
scopes: data.scopes,
site_admin: data.site_admin
};
}
data._cnpm_meta = {
id: user.id,
npm_user: user.isNpmUser,
custom_user: !!data.login,
gmt_create: user.gmt_create,
gmt_modified: user.gmt_modified,
};
this.body = data;
};

View File

@@ -0,0 +1,97 @@
/**!
* cnpmjs.org - controllers/registry/user/update.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:controllers:registry:user:update');
var ensurePasswordSalt = require('./common').ensurePasswordSalt;
var userService = require('../../../services/user');
// 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' ] } }
module.exports = function* updateUser(next) {
var name = this.params.name;
var rev = this.params.rev;
if (!name || !rev) {
return yield* next;
}
debug('update: %s, rev: %s, user.name: %s', name, rev, this.user.name);
if (name !== this.user.name) {
// must auth user first
this.status = 401;
this.body = {
error: 'unauthorized',
reason: 'Name is incorrect.'
};
return;
}
var body = this.request.body || {};
var user = {
name: body.name,
// 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 (!body.password || !user.name || !user.salt || !user.password_sha || !user.email) {
this.status = 422;
this.body = {
error: 'paramError',
reason: 'params missing, name, email or password missing.'
};
return;
}
var result = yield* userService.update(user);
if (!result) {
this.status = 409;
this.body = {
error: 'conflict',
reason: 'Document update conflict.'
};
return;
}
this.status = 201;
this.body = {
ok: true,
id: 'org.couchdb.user:' + user.name,
rev: result.rev
};
};

View File

@@ -0,0 +1,51 @@
/**!
* 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 packageService = require('../../services/package');
// GET /-/by-user/:user
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 tasks = {};
for (var i = 0; i < users.length; i++) {
var username = users[i];
tasks[username] = packageService.listPublicModuleNamesByUser(username);
}
var data = yield tasks;
for (var k in data) {
if (data[k].length === 0) {
data[k] = undefined;
}
}
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,51 +14,51 @@
* Module dependencies.
*/
var Log = require('../proxy/module_log');
var SyncModuleWorker = require('../proxy/sync_module_worker');
var debug = require('debug')('cnpmjs.org:controllers:sync');
var Log = require('../services/module_log');
var SyncModuleWorker = require('./sync_module_worker');
exports.sync = function (req, res, next) {
var username = req.session.name || 'anonymous';
var name = req.params.name;
var publish = req.query.publish === 'true';
var noDep = req.query.nodeps === 'true';
if (publish && !req.session.isAdmin) {
return res.json(403, {
exports.sync = function* () {
var username = this.user.name || 'anonymous';
var name = this.params.name;
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 = {
error: 'no_perms',
reason: 'Only admin can publish'
});
};
return;
}
var options = {
publish: publish,
noDep: noDep,
};
SyncModuleWorker.sync(name, username, options, function (err, result) {
if (err) {
return next(err);
}
if (!result.ok) {
return res.json(result.statusCode, result.pkg);
}
res.json(201, {
ok: true,
logId: result.logId
});
});
var logId = yield* SyncModuleWorker.sync(name, username, options);
debug('sync %s got log id %j', name, logId);
this.status = 201;
this.body = {
ok: true,
logId: logId
};
};
exports.getSyncLog = function (req, res, next) {
var logId = req.params.id;
var name = req.params.name;
var offset = Number(req.query.offset) || 0;
Log.get(logId, function (err, row) {
if (err || !row) {
return next(err);
}
var log = row.log.trim();
if (offset > 0) {
log = log.split('\n').slice(offset).join('\n');
}
res.json(200, {ok: true, log: log});
});
exports.getSyncLog = function* (next) {
var logId = this.params.id;
var offset = Number(this.query.offset) || 0;
var row = yield* Log.get(logId);
if (!row) {
return yield* next;
}
var log = row.log.trim();
if (offset > 0) {
log = log.split('\n').slice(offset).join('\n');
}
this.body = {ok: true, log: log};
};

File diff suppressed because it is too large Load Diff

View File

@@ -15,32 +15,25 @@
* Module dependencies.
*/
var microtime = require('microtime');
var eventproxy = require('eventproxy');
var Total = require('../proxy/total');
var down = require('./download');
var Total = require('../services/total');
var version = require('../package.json').version;
var config = require('../config');
var getDownloadTotal = require('./utils').getDownloadTotal;
var startTime = '' + microtime.now();
var startTime = '' + Date.now();
exports.show = function (req, res, next) {
var ep = eventproxy.create();
ep.fail(next);
module.exports = function* showTotal() {
var r = yield [Total.get(), getDownloadTotal()];
var total = r[0];
var download = r[1];
Total.get(ep.done('total'));
down.total(null, ep.done('download'));
ep.all('total', 'download', function (total, download) {
total.download = download;
total.db_name = 'registry';
total.instance_start_time = startTime;
total.node_version = process.version;
total.app_version = version;
total.donate = 'https://me.alipay.com/imk2';
total.sync_model = config.syncModel;
if (req.query.callback) {
return res.jsonp(total, req.query.callback);
}
res.json(total);
});
total.download = download;
total.db_name = 'registry';
total.instance_start_time = startTime;
total.node_version = process.version;
total.app_version = version;
total.donate = 'https://www.gittip.com/fengmk2';
total.sync_model = config.syncModel;
this.body = total;
};

161
controllers/utils.js Normal file
View File

@@ -0,0 +1,161 @@
/**!
* cnpmjs.org - controllers/utils.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:controllers:utils');
var path = require('path');
var fs = require('fs');
var utility = require('utility');
var ms = require('humanize-ms');
var moment = require('moment');
var downloadTotalService = require('../services/download_total');
var nfs = require('../common/nfs');
var config = require('../config');
var DOWNLOAD_TIMEOUT = ms('10m');
exports.downloadAsReadStream = function* (key) {
var tmpPath = path.join(config.uploadDir,
utility.randomString() + key.replace(/\//g, '-'));
function cleanup() {
debug('cleanup %s', tmpPath);
fs.unlink(tmpPath, utility.noop);
}
debug('downloadAsReadStream() %s to %s', key, tmpPath);
try {
yield nfs.download(key, tmpPath, {timeout: DOWNLOAD_TIMEOUT});
} catch (err) {
debug('downloadAsReadStream() %s to %s error: %s', key, tmpPath, err.stack);
cleanup();
throw err;
}
var tarball = fs.createReadStream(tmpPath);
tarball.once('error', cleanup);
tarball.once('end', cleanup);
return tarball;
};
exports.getDownloadTotal = function* (name) {
var end = moment();
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');
var thisweekStart = end.clone().startOf('week').format('YYYY-MM-DD');
start = start.format('YYYY-MM-DD');
end = end.format('YYYY-MM-DD');
lastweekStart = lastweekStart.format('YYYY-MM-DD');
var method = name ? 'getModuleTotal' : 'getTotal';
var args = [start, end];
if (name) {
args.unshift(name);
}
var rows = yield* downloadTotalService[method].apply(downloadTotalService, args);
var download = {
today: 0,
thisweek: 0,
thismonth: 0,
lastday: 0,
lastweek: 0,
lastmonth: 0,
};
for (var i = 0; i < rows.length; i++) {
var r = rows[i];
if (r.date === end) {
download.today += r.count;
}
if (r.date >= thismonthStart) {
download.thismonth += r.count;
}
if (r.date >= thisweekStart) {
download.thisweek += r.count;
}
if (r.date === lastday) {
download.lastday += r.count;
}
if (r.date >= lastweekStart && r.date <= lastweekEnd) {
download.lastweek += r.count;
}
if (r.date >= start && r.date <= lastmonthEnd) {
download.lastmonth += r.count;
}
}
return download;
};
exports.setLicense = function (pkg) {
var license;
license = pkg.license || pkg.licenses || pkg.licence || pkg.licences;
if (!license) {
return ;
}
if (Array.isArray(license)) {
license = license[0];
}
if (typeof license === 'object') {
pkg.license = {
name: license.name || license.type,
url: license.url
};
}
if (typeof license === 'string') {
if (license.match(/(http|https)(:\/\/)/ig)) {
pkg.license = {
name: license,
url: license
};
} else {
pkg.license = {
url: exports.getOssLicenseUrlFromName(license),
name: license
};
}
}
};
exports.getOssLicenseUrlFromName = function (name) {
var base = 'http://opensource.org/licenses/';
var licenseMap = {
'bsd': 'BSD-2-Clause',
'mit': 'MIT',
'x11': 'MIT',
'mit/x11': 'MIT',
'apache 2.0': 'Apache-2.0',
'apache2': 'Apache-2.0',
'apache 2': 'Apache-2.0',
'apache-2': 'Apache-2.0',
'apache': 'Apache-2.0',
'gpl': 'GPL-3.0',
'gplv3': 'GPL-3.0',
'gplv2': 'GPL-2.0',
'gpl3': 'GPL-3.0',
'gpl2': 'GPL-2.0',
'lgpl': 'LGPL-2.1',
'lgplv2.1': 'LGPL-2.1',
'lgplv2': 'LGPL-2.1'
};
return licenseMap[name.toLowerCase()] ?
base + licenseMap[name.toLowerCase()] : base + name;
};

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 packageService = require('../../services/package');
exports.version = function* () {
var color = 'lightgrey';
var version = 'invalid';
var name = this.params[0];
var latestTag = yield* packageService.getModuleByTag(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);
};

100
controllers/web/dist.js Normal file
View File

@@ -0,0 +1,100 @@
/**!
* cnpmjs.org - controllers/web/dist.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
"use strict";
/**
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:controllers:web:dist');
var mime = require('mime');
var urlparse = require('url').parse;
var distService = require('../../services/dist');
var config = require('../../config');
var downloadAsReadStream = require('../utils').downloadAsReadStream;
function padding(max, current, pad) {
pad = pad || ' ';
var left = max - current;
var str = '';
for (var i = 0; i < left; i++) {
str += pad;
}
return str;
}
exports.list = function* (next) {
var params = this.params;
var url = params[0];
if (!url) {
// GET /dist => /dist/
return this.redirect('/dist/');
}
var isDir = url[url.length - 1] === '/';
if (!isDir) {
return yield* download.call(this, next);
}
var items = yield* distService.listdir(url);
if (url === '/') {
// phantomjs/
items.push({
name: 'phantomjs/',
date: '',
});
}
yield this.render('dist', {
title: 'Mirror index of ' + config.disturl + url,
disturl: config.disturl,
dirname: url,
items: items,
padding: padding
});
};
function* download(next) {
var fullname = this.params[0];
var info = yield* distService.getfile(fullname);
debug('download %s got %j', fullname, info);
if (!info || !info.url) {
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' && info.size > 0) {
this.length = info.size;
}
this.type = mime.lookup(info.url);
if (attachment) {
this.attachment(info.name);
}
if (info.sha1) {
this.etag = info.sha1;
}
this.body = yield* downloadAsReadStream(info.url);
}

View File

@@ -1,223 +0,0 @@
/*!
* cnpmjs.org - controllers/web/package.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 giturl = require('giturl');
var moment = require('moment');
var eventproxy = require('eventproxy');
var semver = require('semver');
var marked = require('marked');
var gravatar = require('gravatar');
var humanize = require('humanize-number');
var config = require('../../config');
var Module = require('../../proxy/module');
var down = require('../download');
var sync = require('../sync');
var Log = require('../../proxy/module_log');
var setDownloadURL = require('../../lib/common').setDownloadURL;
exports.display = function (req, res, next) {
var params = req.params;
var name = params.name;
var tag = params.version;
var ep = eventproxy.create();
ep.fail(next);
if (tag) {
var version = semver.valid(tag);
if (version) {
Module.get(name, version, ep.done('pkg'));
} else {
Module.getByTag(name, tag, ep.done('pkg'));
}
} else {
Module.getByTag(name, 'latest', ep.done('pkg'));
}
down.total(name, ep.done('download'));
ep.all('pkg', 'download', function (pkg, download) {
if (!pkg || !pkg.package) {
return next();
}
pkg.package.fromNow = moment(pkg.publish_time).fromNow();
pkg = pkg.package;
pkg.readme = marked(pkg.readme || '');
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);
}
}
}
if (pkg.contributors) {
// registry.cnpmjs.org/compressible
if (!Array.isArray(pkg.contributors)) {
pkg.contributors = [pkg.contributors];
}
for (var i = 0; i < pkg.contributors.length; i++) {
var contributor = pkg.contributors[i];
if (contributor.email) {
contributor.gravatar = gravatar.url(contributor.email, {s: '50', d: 'retro'}, false);
}
if (config.packagePageContributorSearch || !contributor.url) {
contributor.url = '/~' + encodeURIComponent(contributor.name);
}
}
}
if (pkg.repository && pkg.repository.url) {
pkg.repository.weburl = giturl.parse(pkg.repository.url);
}
setLicense(pkg);
for (var k in download) {
download[k] = humanize(download[k]);
}
setDownloadURL(pkg, req, config.registryHost);
res.render('package', {
title: 'Package - ' + pkg.name,
package: pkg,
download: download
});
});
};
exports.search = function (req, res, next) {
var params = req.params;
var word = req.params.word;
Module.search(word, function (err, result) {
if (err) {
return next(err);
}
res.render('search', {
title: 'Keyword - ' + word,
keyword: word,
packages: result.searchMatchs,
keywords: result.keywordMatchs,
});
});
};
exports.rangeSearch = function (req, res, next) {
var startKey = req.query.startkey || '';
if (startKey[0] === '"') {
startKey = startKey.substring(1);
}
if (startKey[startKey.length - 1] === '"') {
startKey = startKey.substring(0, startKey.length - 1);
}
var limit = Number(req.query.limit) || 20;
Module.search(startKey, {limit: limit}, function (err, result) {
if (err) {
return next(err);
}
var packages = result.searchMatchs.concat(result.keywordMatchs);
var rows = [];
for (var i = 0; i < packages.length; i++) {
var p = packages[i];
var row = {
key: p.name,
count: 1,
value: {
name: p.name,
description: p.description,
}
};
rows.push(row);
}
res.json({
rows: rows
});
});
};
exports.displaySync = function (req, res, next) {
var name = req.params.name || req.query.name;
res.render('sync', {
name: name,
title: 'Sync - ' + name
});
};
function setLicense(pkg) {
var license;
license = pkg.license || pkg.licenses || pkg.licence || pkg.licences;
if (!license) {
return ;
}
if (Array.isArray(license)) {
license = license[0];
}
if (typeof license === 'object') {
pkg.license = {
name: license.name || license.type,
url: license.url
};
}
if (typeof license === 'string') {
if (license.match(/(http|https)(:\/\/)/ig)) {
pkg.license = {
name: license,
url: license
};
} else {
pkg.license = {
url: getOssLicenseUrlFromName(license),
name: license
};
}
}
}
exports.setLicense = setLicense;
function getOssLicenseUrlFromName(name) {
var base = 'http://opensource.org/licenses/';
var licenseMap = {
'bsd': 'BSD-2-Clause',
'mit': 'MIT',
'x11': 'MIT',
'mit/x11': 'MIT',
'apache 2.0': 'Apache-2.0',
'apache2': 'Apache-2.0',
'apache 2': 'Apache-2.0',
'apache-2': 'Apache-2.0',
'apache': 'Apache-2.0',
'gpl': 'GPL-3.0',
'gplv3': 'GPL-3.0',
'gplv2': 'GPL-2.0',
'gpl3': 'GPL-3.0',
'gpl2': 'GPL-2.0',
'lgpl': 'LGPL-2.1',
'lgplv2.1': 'LGPL-2.1',
'lgplv2': 'LGPL-2.1'
};
return licenseMap[name.toLowerCase()] ?
base + licenseMap[name.toLowerCase()] : base + name;
}

View File

@@ -0,0 +1,33 @@
/**!
* cnpmjs.org - controllers/web/package/list_privates.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var packageService = require('../../../services/package');
var config = require('../../../config');
module.exports = function* listPrivates() {
var tasks = {};
for (var i = 0; i < config.scopes.length; i++) {
var scope = config.scopes[i];
tasks[scope] = packageService.listPrivateModulesByScope(scope);
}
var scopes = yield tasks;
yield this.render('private', {
title: 'private packages',
scopes: scopes
});
};

View File

@@ -0,0 +1,54 @@
/**!
* cnpmjs.org - controllers/web/package/search.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:controllers:web:package:search');
var packageService = require('../../../services/package');
module.exports = function* search() {
var params = this.params;
var word = params.word || params[0];
debug('search %j', word);
var result = yield* packageService.search(word);
var match = null;
for (var i = 0; i < result.searchMatchs.length; i++) {
var p = result.searchMatchs[i];
if (p.name === word) {
match = p;
break;
}
}
// return a json result
if (this.query && this.query.type === 'json') {
this.body = {
keyword: word,
match: match,
packages: result.searchMatchs,
keywords: result.keywordMatchs,
};
this.type = 'application/json; charset=utf-8';
return;
}
yield this.render('search', {
title: 'Keyword - ' + word,
keyword: word,
match: match,
packages: result.searchMatchs,
keywords: result.keywordMatchs,
});
};

View File

@@ -0,0 +1,49 @@
/**!
* cnpmjs.org - controllers/web/package/search_range.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var packageService = require('../../../services/package');
module.exports = function* searchRange() {
var startKey = this.query.startkey || '';
if (startKey[0] === '"') {
startKey = startKey.substring(1);
}
if (startKey[startKey.length - 1] === '"') {
startKey = startKey.substring(0, startKey.length - 1);
}
var limit = Number(this.query.limit) || 20;
var result = yield* packageService.search(startKey, {limit: limit});
var packages = result.searchMatchs.concat(result.keywordMatchs);
var rows = [];
for (var i = 0; i < packages.length; i++) {
var p = packages[i];
var row = {
key: p.name,
count: 1,
value: {
name: p.name,
description: p.description,
}
};
rows.push(row);
}
this.body = {
rows: rows
};
};

View File

@@ -0,0 +1,188 @@
/**!
* cnpmjs.org - controllers/web/package/show.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:controllers:web:package:show');
var bytes = require('bytes');
var giturl = require('giturl');
var moment = require('moment');
var semver = require('semver');
var marked = require('marked');
var gravatar = require('gravatar');
var humanize = require('humanize-number');
var config = require('../../../config');
var utils = require('../../utils');
var setDownloadURL = require('../../../lib/common').setDownloadURL;
var packageService = require('../../../services/package');
module.exports = function* show(next) {
var params = this.params;
// 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;
var version = semver.valid(tag || '');
if (version) {
getPackageMethod = 'getModule';
getPackageArgs = [name, version];
} else {
getPackageMethod = 'getModuleByTag';
getPackageArgs = [name, tag || 'latest'];
}
var pkg = yield packageService[getPackageMethod].apply(packageService, getPackageArgs);
if (!pkg || !pkg.package) {
// check if unpublished
var unpublishedInfo = yield* packageService.getUnpublishedModule(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 [
utils.getDownloadTotal(name),
packageService.listDependents(name),
packageService.listStarUserNames(name),
packageService.listMaintainers(name)
];
var download = r[0];
var dependents = r[1];
var users = r[2];
var maintainers = r[3];
pkg.package.fromNow = moment(pkg.publish_time).fromNow();
pkg = pkg.package;
pkg.users = users;
pkg.readme = marked(pkg.readme || '');
if (!pkg.readme) {
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'}, true);
}
}
}
if (pkg.contributors) {
// registry.cnpmjs.org/compressible
if (!Array.isArray(pkg.contributors)) {
pkg.contributors = [pkg.contributors];
}
for (var i = 0; i < pkg.contributors.length; i++) {
var contributor = pkg.contributors[i];
if (contributor.email) {
contributor.gravatar = gravatar.url(contributor.email, {s: '50', d: 'retro'}, true);
}
if (config.packagePageContributorSearch || !contributor.url) {
contributor.url = '/~' + encodeURIComponent(contributor.name);
}
}
}
if (pkg.repository && pkg.repository.url) {
pkg.repository.weburl = giturl.parse(pkg.repository.url) || pkg.repository.url;
}
utils.setLicense(pkg);
for (var k in download) {
download[k] = humanize(download[k]);
}
setDownloadURL(pkg, this, config.registryHost);
pkg.dependents = dependents;
if (pkg.dist) {
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,
download: download
});
};

View File

@@ -0,0 +1,27 @@
/**!
* cnpmjs.org - controllers/web/package/show_sync.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
module.exports = function* showSync() {
var name = this.params.name || this.params[0] || this.query.name;
if (!name) {
return this.redirect('/');
}
yield this.render('sync', {
name: name,
title: 'Sync - ' + name,
});
};

View File

@@ -1,45 +0,0 @@
/*!
* cnpmjs.org - controllers/web/package.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 Module = require('../../proxy/module');
var User = require('../../proxy/user');
var eventproxy = require('eventproxy');
exports.display = function (req, res, next) {
var name = req.params.name;
var ep = eventproxy.create();
ep.fail(next);
Module.listByAuthor(name, ep.done('packages'));
User.get(name, ep.done('user'));
ep.all('packages', 'user', function (packages, user) {
//because of sync, maybe no this user in database,
//but his packages in this registry
if (!user && !packages.length) {
return next();
}
user = {
name: name,
email: user && user.email
};
return res.render('profile', {
title: 'User - ' + name,
packages: packages || [],
user: user
});
});
};

View File

@@ -0,0 +1,70 @@
/**!
* cnpmjs.org - controllers/web/user/show.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var config = require('../../../config');
var packageService = require('../../../services/package');
var userService = require('../../../services/user');
var common = require('../../../lib/common');
module.exports = function* showUser(next) {
var name = this.params.name;
var isAdmin = common.isAdmin(name);
var scopes = config.scopes || [];
var user;
var r = yield [packageService.listModulesByUser(name), userService.getAndSave(name)];
var packages = r[0];
var user = r[1];
if (!user && !packages.length) {
return yield* next;
}
user = user || {};
var data = {
name: name,
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: data,
lastModified: user.gmt_modified,
isAdmin: isAdmin,
scopes: scopes
});
};

View File

@@ -1,4 +1,4 @@
/*!
/**!
* cnpmjs.org - dispatch.js
*
* Copyright(c) cnpmjs.org and other contributors.
@@ -15,45 +15,52 @@
* Module dependencies.
*/
var childProcess = require('child_process');
var path = require('path');
var util = require('util');
var fs = require('fs');
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
});
cluster.on('fork', function (worker) {
console.log('[%s] [worker:%d] new worker start', new Date(), worker.process.pid);
});
cluster.on('disconnect', function (worker) {
var w = cluster.fork();
console.error('[%s] [master:%s] wroker:%s disconnect! new worker:%s fork',
new Date(), process.pid, worker.process.pid, w.process.pid);
});
cluster.on('exit', function (worker, code, signal) {
var exitCode = worker.process.exitCode;
var err = new Error(util.format('worker %s died (code: %s, signal: %s)', worker.process.pid, exitCode, signal));
err.name = 'WorkerDiedError';
console.error(err);
});
var numCPUs = require('os').cpus().length;
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
forkWorker();
if (config.syncModel !== 'none') {
forkSyncer();
}
childProcess.fork(syncPath);
} else {
require(workerPath);
require(syncPath);
if (config.syncModel !== 'none') {
require(syncPath);
}
}
function forkWorker() {
cfork({
exec: workerPath,
count: config.numCPUs,
}).on('fork', function (worker) {
console.log('[%s] [worker:%d] new worker start', Date(), worker.process.pid);
}).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);
});
}
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',
@@ -9,12 +9,17 @@ CREATE TABLE `user` (
`roles` varchar(200) NOT NULL DEFAULT '[]',
`rev` varchar(40) NOT NULL,
`email` varchar(400) NOT NULL,
`json` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'json details',
`npm_user` tinyint(1) DEFAULT '0' COMMENT 'user sync from npm or not, 1: true, other: false',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
KEY `gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='user base info';
-- ALTER 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',
@@ -25,7 +30,37 @@ CREATE TABLE `module_keyword` (
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module keyword';
CREATE TABLE `module` (
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',
`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='module star';
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',
@@ -51,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',
@@ -63,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',
@@ -72,12 +107,25 @@ CREATE TABLE `tag` (
`version` varchar(30) NOT NULL COMMENT 'module version',
`module_id` bigint(20) unsigned NOT NULL COMMENT 'module id',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`, `tag`)
UNIQUE KEY `name` (`name`, `tag`),
KEY `gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module tag';
-- ALTER TABLE `tag` ADD `module_id` BIGINT( 20 ) UNSIGNED NOT NULL;
-- 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',
@@ -91,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'
@@ -101,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',
@@ -112,3 +162,40 @@ CREATE TABLE `download_total` (
UNIQUE KEY `date_name` (`date`, `name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module download total info';
-- ALTER TABLE `download_total` CHANGE `name` `name` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
CREATE TABLE 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',
`deps` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'which module depend on this module',
PRIMARY KEY (`id`),
UNIQUE KEY `name_deps` (`name`,`deps`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module deps';
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',
`name` varchar(200) NOT NULL COMMENT 'dir 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 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',
`name` varchar(100) NOT NULL COMMENT 'file 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';

View File

@@ -1,5 +1,24 @@
# npm publish flow
## Flows
1. try to put package.json and tgz, maybe base64 tgz body
2. if new version not exists, publish success
3. if new version exists, 409, try to get full package info with ?write=true
4. if new version had publish, show: "Update the 'version' field in package.json and try again."
```bash
$ cnpm publish
npm http PUT http://r.cnpmjs.org/cnpmjs.org
npm http 409 http://r.cnpmjs.org/cnpmjs.org
npm http GET http://r.cnpmjs.org/cnpmjs.org?write=true
npm http 200 http://r.cnpmjs.org/cnpmjs.org?write=true
npm ERR! publish fail Cannot publish over existing version.
npm ERR! publish fail Update the 'version' field in package.json and try again.
```
## Details
code: https://github.com/isaacs/npm-registry-client/blob/master/lib/publish.js
* couch login if token not exists: [couch-login](https://github.com/isaacs/couch-login)

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

@@ -103,13 +103,13 @@ npm install -g cnpm
alias lnpm='cnpm --registry=http://localhost:7001\
--registryweb=http://localhost:7002\
--cache=$HOME/.npm/.cache/lnpm\
--disturl=http://dist.u.qiniudn.com\
--disturl=http://cnpmjs.org/dist\
--userconfig=$HOME/.lnpmrc'
#or put this in .zshrc or .bashrc
echo "#lnpm alias\nalias lnpm='cnpm --registry=http://localhost:7001\
--registryweb=http://localhost:7002\
--cache=$HOME/.npm/.cache/lnpm\
--disturl=http://dist.u.qiniudn.com\
--disturl=http://cnpmjs.org/dist\
--userconfig=$HOME/.lnpmrc'" >> $HOME/.zshrc && source $HOME/.zshrc
```

View File

@@ -1,23 +1,14 @@
# cnpmjs.org: Private npm registry and web for Enterprise
# cnpmjs.org: Private npm registry and web for Company
[![Build Status](https://secure.travis-ci.org/cnpm/cnpmjs.org.png)](http://travis-ci.org/cnpm/cnpmjs.org) [![Coverage Status](https://coveralls.io/repos/cnpm/cnpmjs.org/badge.png)](https://coveralls.io/r/cnpm/cnpmjs.org) [![Dependency Status](https://gemnasium.com/cnpm/cnpmjs.org.png)](https://gemnasium.com/cnpm/cnpmjs.org)
[![NPM](https://nodei.co/npm/cnpmjs.org.png?downloads=true&stars=true)](https://nodei.co/npm/cnpmjs.org/)
## What is this?
> Private npm registry and web for Enterprise, base on MySQL and [Simple Store Service](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide).
@[JacksonTian](https://github.com/JacksonTian/) had a talk about [private npm](https://speakerdeck.com/jacksontian/qi-ye-ji-node-dot-jskai-fa).
## Install your private npm registry
@see [Install and Get Started](/install).
So `cnpm` is meaning: **Company npm**.
## Registry
* 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 [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>
@@ -114,32 +105,42 @@ $(function () {
});
</script>
## cnpm cli
## Version Badge
alias it:
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:
```
npm install -g cnpm --registry=http://r.cnpmjs.org
```
Or you can alias NPM to use it:
```bash
alias cnpm="npm --registry=http://r.cnpmjs.org \
--cache=$HOME/.npm/.cache/cnpm \
--disturl=http://dist.u.qiniudn.com \
--disturl=http://cnpmjs.org/dist \
--userconfig=$HOME/.cnpmrc"
#Or alias it in .bashrc or .zshrc
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=http://r.cnpmjs.org \
--cache=$HOME/.npm/.cache/cnpm \
--disturl=http://dist.u.qiniudn.com \
--disturl=http://cnpmjs.org/dist \
--userconfig=$HOME/.cnpmrc"' >> ~/.zshrc && source ~/.zshrc
```
Or you can just use our `cnpm` cli:
```bash
$ npm install cnpm -g
```
### install
Install package from [r.cnpmjs.org](http://r.cnpmjs.org). When isntall a package or version not exist, it will try to install from official registry([registry.npmjs.org](http://registry.npmjs.org)), and sync this package to cnpm in the backend.
Install package from [r.cnpmjs.org](http://r.cnpmjs.org). When installing a package or version does not exist, it will try to install from the official registry([registry.npmjs.org](http://registry.npmjs.org)), and sync this package to cnpm in the backend.
```
$ cnpm install [name]
@@ -180,33 +181,10 @@ $ cnpm info cnpm
@see Github [Issues](https://github.com/cnpm/cnpmjs.org/issues)
## Authors
## Histories
Release [History](/history).
```bash
$ git summary
project : cnpmjs.org
repo age : 7 weeks
active : 132 days
commits : 315
files : 88
authors :
190 fengmk2 60.3%
122 dead_horse 38.7%
2 4simple 0.6%
1 Alsotang 0.3%
```
## 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)
[cnpmjs.org]: http://cnpmjs.org/
[registry.cnpmjs.org]: http://registry.cnpmjs.org/

View File

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

View File

@@ -21,6 +21,7 @@ var util = require('util');
exports.getTarballFilepath = function (filename) {
// ensure download file path unique
// TODO: not only .tgz, and also other extname
var name = filename.replace(/\.tgz$/, '.' + crypto.randomBytes(16).toString('hex') + '.tgz');
return path.join(config.uploadDir, name);
};
@@ -29,11 +30,11 @@ exports.getCDNKey = function (name, filename) {
return '/' + name + '/-/' + filename;
};
exports.setDownloadURL = function (pkg, req, host) {
exports.setDownloadURL = function (pkg, ctx, host) {
if (pkg.dist) {
host = host || req.headers.host;
host = host || ctx.host;
pkg.dist.tarball = util.format('%s://%s/%s/download/%s-%s.tgz',
req.connection.encrypted ? 'https' : 'http',
ctx.protocol,
host, pkg.name, pkg.name, pkg.version);
}
};
@@ -42,12 +43,12 @@ exports.isAdmin = function (username) {
return typeof config.admins[username] === 'string';
};
exports.isMaintainer = function (req, maintainers) {
if (req.session.isAdmin) {
exports.isMaintainer = function (user, maintainers) {
if (user.isAdmin) {
return true;
}
var username = req.session.name;
var username = user.name;
maintainers = maintainers || [];
var match = maintainers.filter(function (item) {
return item.name === username;

View File

@@ -15,55 +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');
module.exports = function (options) {
return function auth(req, res, next) {
if (!req.session) {
// redis crash
req.session = {};
return next();
}
req.session.onlySync = config.enablePrivate ? true : false;
if (req.session.name) {
req.session.isAdmin = common.isAdmin(req.session.name);
debug('auth exists user: %s, onlySync: %s, isAdmin: %s, headers: %j',
req.session.name, req.session.onlySync, req.session.isAdmin, req.headers);
return next();
}
var authorization = (req.headers.authorization || '').split(' ')[1] || '';
/**
* Parse the request authorization
* get the real user
*/
module.exports = function () {
return function* auth(next) {
this.user = {};
var authorization = (this.get('authorization') || '').split(' ')[1] || '';
authorization = authorization.trim();
debug('%s %s with %j', this.method, this.url, authorization);
if (!authorization) {
return next();
return yield* next;
}
authorization = new Buffer(authorization, 'base64').toString().split(':');
if (authorization.length !== 2) {
return next();
return yield* next;
}
var username = authorization[0];
var password = authorization[1];
User.auth(username, password, function (err, row) {
if (err) {
return next(err);
}
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;
}
if (!row) {
debug('auth fail user: %j, headers: %j', row, req.headers);
req.session.name = null;
req.session.isAdmin = false;
return next();
}
if (!row) {
debug('auth fail user: %j, headers: %j', row, this.header);
return yield* next;
}
req.session.name = row.name;
req.session.isAdmin = common.isAdmin(req.session.name);
debug('auth pass user: %j, onlySync: %s, isAdmin: %s, headers: %j',
row, req.session.onlySync, req.session.isAdmin, req.headers);
next();
});
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;
};
};

42
middleware/editable.js Normal file
View File

@@ -0,0 +1,42 @@
/**!
* cnpmjs.org - middleware/editable.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var packageService = require('../services/package');
// admin or module's maintainer can modified the module
module.exports = function* editable(next) {
var username = this.user && this.user.name;
var moduleName = this.params.name || this.params[0];
if (username && moduleName) {
if (this.user.isAdmin) {
return yield* next;
}
var isMaintainer = yield* packageService.isMaintainer(moduleName, username);
if (isMaintainer) {
return yield* next;
}
}
this.status = 403;
var message = 'not authorized to modify ' + moduleName;
if (username) {
message = username + ' ' + message;
}
this.body = {
error: 'forbidden user',
reason: message
};
};

34
middleware/limit.js Normal file
View File

@@ -0,0 +1,34 @@
/**!
* cnpmjs.org - middleware/limit.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
'use strict';
/**
* Module dependencies.
*/
var config = require('../config');
var limit = require('koa-limit');
var store = require('../common/redis');
var limitConfig = config.limit;
if (!limitConfig.enable) {
module.exports = function *ignoreLimit(next) {
yield *next;
};
} else {
if (!config.debug) {
limitConfig.store = store;
}
module.exports = limit(limitConfig);
}

View File

@@ -14,12 +14,29 @@
* Module dependencies.
*/
module.exports = function login(req, res, next) {
if (!req.session.name) {
return res.json(401, {
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',
reason: 'Login first.'
});
};
return;
}
next();
yield *next;
};

View File

@@ -22,10 +22,11 @@ var template = '<?xml version="1.0" encoding="UTF-8"?>\
<Url method="get" type="text/html" template="http://${host}/browse/keyword/{searchTerms}"/>\
</OpenSearchDescription>';
var lastModifyDate = new Date();
module.exports = function publishable(req, res, next) {
res.charset = res.charset || 'utf-8';
res.setHeader('Content-Type', 'text/xml');
res.send(template.replace('${host}', req.headers.host));
module.exports = function *publishable(next) {
if (this.path === '/opensearch.xml') {
this.type = 'text/xml';
this.charset = 'utf-8';
this.body = template.replace('${host}', this.host);
}
yield *next;
};

View File

@@ -14,13 +14,89 @@
* Module dependencies.
*/
module.exports = function publishable(req, res, next) {
if (req.session.onlySync && !req.session.isAdmin) {
// private mode, only admin user can publish
return res.json(403, {
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) {
this.status = 403;
this.body = {
error: 'no_perms',
reason: 'Private mode enable, only admin can publish this module'
});
};
return;
}
// 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;
}
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: %s', scope, ctx.user.scopes.join(', '))
};
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

@@ -0,0 +1,32 @@
/**!
* cnpmjs.org - middleware/registry_not_found.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
'use strict';
/**
* Module dependencies.
*/
module.exports = function* notFound(next) {
yield* next;
if (this.status && this.status !== 404) {
return;
}
if (this.body && this.body.name) {
return;
}
this.status = 404;
this.body = {
error: 'not_found',
reason: 'document not found'
};
};

29
middleware/static.js Normal file
View File

@@ -0,0 +1,29 @@
/**!
* cnpmjs.org - middleware/static.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var path = require('path');
var middlewares = require('koa-middlewares');
var config = require('../config');
var staticDir = path.join(path.dirname(__dirname), 'public');
module.exports = middlewares.staticCache(staticDir, {
buffer: config.debug ? false : true,
maxAge: config.debug ? 0 : 60 * 60 * 24 * 7,
alas: {
'/favicon.ico': '/favicon.png'
}
});

View File

@@ -1,4 +1,4 @@
/*!
/**!
* cnpmjs.org - middleware/sync_by_install.js
*
* Copyright(c) cnpmjs.org and other contributors.
@@ -14,32 +14,36 @@
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:middleware:sync_by_install');
var config = require('../config');
/**
* req.session.allowSync - allow sync triggle by cnpm install
* this.allowSync - allow sync triggle by cnpm install
*/
module.exports = function (req, res, next) {
module.exports = function* syncByInstall(next) {
this.allowSync = false;
if (!config.syncByInstall || !config.enablePrivate) {
// only config.enablePrivate should enable sync on install
return next();
return yield* next;
}
// request not by node, consider it request from web
if (req.headers['user-agent'] && req.headers['user-agent'].indexOf('node') !== 0) {
return next();
var ua = this.get('user-agent');
if (!ua || ua.indexOf('node') < 0) {
return yield* next;
}
req.session.allowSync = true;
if (req.session.isAdmin) {
// if current user is admin, should not enable auto sync on install, because it would be unpublish
req.session.allowSync = false;
// if request with `/xxx?write=true`, meaning the read request using for write
if (this.query.write) {
return yield* next;
}
// TODO: allow sync will let publish sync package...
req.session.allowSync = false;
var name = this.params.name || this.params[0];
debug('%s allowSync: %s', req.session.name, req.session.allowSync);
next();
// scoped package dont sync
if (name && name[0] === '@') {
return yield* next;
}
this.allowSync = true;
yield* next;
};

View File

@@ -0,0 +1,53 @@
/**!
* cnpmjs.org - middleware/web_not_found.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 debug = require('debug')('cnpmjs.org:middleware:web_not_found');
module.exports = function* notFound(next) {
yield* next;
if (this.status && this.status !== 404) {
return;
}
if (this.body) {
return;
}
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]);
}
// package not found
m = /\/package\/([\w\-\_\.]+)\/?$/.exec(this.url);
var name = null;
var title = '404: Page Not Found';
if (m) {
name = m[1];
title = name + ' Not Found';
}
this.status = 404;
yield* this.render('404', {
title: title,
name: name
});
};

View File

@@ -0,0 +1,121 @@
/**!
* cnpmjs.org - models/_module_maintainer_class_methods.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
module.exports = {
listModuleNamesByUser: function* (user) {
var rows = yield this.findAll({
attributrs: ['name'],
where: {
user: user
}
});
return rows.map(function (row) {
return row.name;
});
},
listMaintainers: function* (name) {
var rows = yield this.findAll({
attributrs: ['user'],
where: {
name: name
}
});
return rows.map(function (row) {
return row.user;
});
},
addMaintainer: function* (name, user) {
var row = yield this.find({
where: {
user: user,
name: name
}
});
if (!row) {
row = yield this.build({
user: user,
name: name
}).save();
}
return row;
},
addMaintainers: function* (name, users) {
var tasks = [];
for (var i = 0; i < users.length; i++) {
tasks.push(this.addMaintainer(name, users[i]));
}
return yield tasks;
},
removeMaintainers: function* (name, users) {
// removeMaintainers(name, oneUserName)
if (typeof users === 'string') {
users = [users];
}
if (users.length === 0) {
return;
}
yield this.destroy({
where: {
name: name,
user: users,
}
});
},
removeAllMaintainers: function* (name) {
yield this.destroy({
where: {
name: name
}
});
},
updateMaintainers: function* (name, users) {
// maintainers should be [name1, name2, ...] format
// find out the exists maintainers then remove the deletes and add the left
if (users.length === 0) {
return {
add: [],
remove: []
};
}
var exists = yield* this.listMaintainers(name);
var addUsers = [];
var removeUsers = [];
for (var i = 0; i < exists.length; i++) {
var username = exists[i];
if (users.indexOf(username) === -1) {
removeUsers.push(username);
}
}
for (var i = 0; i < users.length; i++) {
var username = users[i];
if (exists.indexOf(username) === -1) {
addUsers.push(username);
}
}
yield [
this.addMaintainers(name, addUsers),
// make sure all add users success then remove users
this.removeMaintainers(name, removeUsers),
];
return {
add: addUsers,
remove: removeUsers
};
},
};

64
models/dist_dir.js Normal file
View File

@@ -0,0 +1,64 @@
/**!
* cnpmjs.org - models/dist_dir.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
/*
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',
`name` varchar(200) NOT NULL COMMENT 'dir name',
`parent` varchar(200) NOT NULL COMMENT 'parent dir' DEFAULT '/',
`date` varchar(20) COMMENT '02-May-2014 01:06',
PRIMARY KEY (`id`),
UNIQUE KEY `dist_dir_parent_name` (`parent`, `name`),
KEY `dist_dir_gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='dist dir info';
*/
module.exports = function (sequelize, DataTypes) {
return sequelize.define('DistDir', {
name: {
type: DataTypes.STRING(200),
allowNull: false,
comment: 'dir name',
},
parent: {
type: DataTypes.STRING(200),
allowNull: false,
defaultValue: '/',
comment: 'parent dir',
},
date: {
type: DataTypes.STRING(20),
allowNull: false,
comment: '02-May-2014 01:06'
}
}, {
tableName: 'dist_dir',
comment: 'dist dir info',
indexes: [
{
unique: true,
fields: ['parent', 'name']
},
{
fields: ['gmt_modified']
}
],
classMethods: {
}
});
};

82
models/dist_file.js Normal file
View File

@@ -0,0 +1,82 @@
/**!
* cnpmjs.org - models/dist_file.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
/*
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',
`name` varchar(100) NOT NULL COMMENT 'file 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 `dist_file_parent_name` (`parent`, `name`),
KEY `dist_file_gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='dist file info';
*/
module.exports = function (sequelize, DataTypes) {
return sequelize.define('DistFile', {
name: {
type: DataTypes.STRING(200),
allowNull: false,
comment: 'dir name',
},
parent: {
type: DataTypes.STRING(200),
allowNull: false,
defaultValue: '/',
comment: 'parent dir',
},
date: {
type: DataTypes.STRING(20),
allowNull: false,
comment: '02-May-2014 01:06'
},
size: {
type: DataTypes.INTEGER(10).UNSIGNED,
allowNull: false,
defaultValue: 0,
comment: 'file size'
},
sha1: {
type: DataTypes.STRING(40),
allowNull: false,
comment: 'sha1 hex value'
},
url: {
type: DataTypes.STRING(2048),
allowNull: false
}
}, {
tableName: 'dist_file',
comment: 'dist file info',
indexes: [
{
unique: true,
fields: ['parent', 'name']
},
{
fields: ['gmt_modified']
}
],
classMethods: {
}
});
};

59
models/download_total.js Normal file
View File

@@ -0,0 +1,59 @@
/**!
* cnpmjs.org - models/download_total.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
// 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',
// `date` varchar(10) NOT NULL COMMENT 'YYYY-MM-DD format',
// `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
// `count` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'download count',
// PRIMARY KEY (`id`),
// UNIQUE KEY `download_total_date_name` (`date`, `name`)
// ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module download total info';
module.exports = function (sequelize, DataTypes) {
return sequelize.define('DownloadTotal', {
date: {
type: DataTypes.STRING(10),
allowNull: false,
comment: 'YYYY-MM-DD format',
},
name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'module name',
},
count: {
type: DataTypes.BIGINT(20).UNSIGNED,
allowNull: false,
defaultValue: 0,
comment: 'download count',
}
}, {
tableName: 'download_total',
comment: 'module download total info',
indexes: [
{
unique: true,
fields: ['date', 'name']
}
],
classMethods: {
}
});
};

49
models/index.js Normal file
View File

@@ -0,0 +1,49 @@
/**!
* cnpmjs.org - models/index.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var path = require('path');
var sequelize = require('../common/sequelize');
function load(name) {
return sequelize.import(path.join(__dirname, name));
}
module.exports = {
sequelize: sequelize,
Module: load('module'),
ModuleLog: load('module_log'),
ModuleStar: load('module_star'),
ModuleKeyword: load('module_keyword'),
ModuleDependency: load('module_deps'),
ModuleMaintainer: load('module_maintainer'),
ModuleUnpublished: load('module_unpublished'),
NpmModuleMaintainer: load('npm_module_maintainer'),
Tag: load('tag'),
User: load('user'),
Total: load('total'),
DownloadTotal: load('download_total'),
DistFile: load('dist_file'),
DistDir: load('dist_dir'),
query: function* (sql, args) {
return yield this.sequelize.query(sql, null, {raw: true}, args);
},
queryOne: function* (sql, args) {
var rows = yield* this.query(sql, args);
return rows && rows[0];
}
};

48
models/init_script.js Normal file
View File

@@ -0,0 +1,48 @@
/**!
* cnpmjs.org - models/init_script.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');
config.database.logging = console.log;
// $ node --harmony models/init_script.js <force> <dialect>
var force = process.argv[2] === 'true';
var dialect = process.argv[3];
if (dialect) {
config.database.dialect = dialect;
}
var models = require('./');
models.sequelize.sync({ force: force })
.then(function () {
models.Total.init(function (err) {
if (err) {
console.error('[models/init_script.js] sequelize init fail');
console.error(err);
throw err;
} else {
console.log('[models/init_script.js] `%s` sequelize sync and init success',
config.database.dialect);
process.exit(0);
}
});
})
.catch(function (err) {
console.error('[models/init_script.js] sequelize sync fail');
console.error(err);
throw err;
});

110
models/module.js Normal file
View File

@@ -0,0 +1,110 @@
/**!
* cnpmjs.org - models/module.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var utils = require('./utils');
/*
CREATE TABLE IF NOT EXISTS `module` (
`id` INTEGER NOT NULL auto_increment ,
`author` VARCHAR(100) NOT NULL,
`name` VARCHAR(100) NOT NULL,
`version` VARCHAR(30) NOT NULL,
`description` LONGTEXT,
`package` LONGTEXT,
`dist_shasum` VARCHAR(100),
`dist_tarball` VARCHAR(2048),
`dist_size` INTEGER UNSIGNED NOT NULL DEFAULT 0,
`publish_time` BIGINT(20) UNSIGNED,
`gmt_create` DATETIME NOT NULL,
`gmt_modified` DATETIME NOT NULL,
PRIMARY KEY (`id`)
)
COMMENT 'module info' ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_general_ci;
CREATE UNIQUE INDEX `module_name_version` ON `module` (`name`, `version`);
CREATE INDEX `module_gmt_modified` ON `module` (`gmt_modified`);
CREATE INDEX `module_publish_time` ON `module` (`publish_time`);
CREATE INDEX `module_author` ON `module` (`author`);
*/
module.exports = function (sequelize, DataTypes) {
return sequelize.define('Module', {
author: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'first maintainer name'
},
name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'module name'
},
version: {
type: DataTypes.STRING(30),
allowNull: false,
comment: 'module version'
},
description: {
type: DataTypes.LONGTEXT,
},
package: {
type: DataTypes.LONGTEXT,
comment: 'package.json',
},
dist_shasum: {
type: DataTypes.STRING(100),
allowNull: true,
},
dist_tarball: {
type: DataTypes.STRING(2048),
allowNull: true,
},
dist_size: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
defaultValue: 0,
},
publish_time: {
type: DataTypes.BIGINT(20).UNSIGNED,
allowNull: true,
}
}, {
tableName: 'module',
comment: 'module info',
indexes: [
{
unique: true,
fields: ['name', 'version']
},
{
fields: ['gmt_modified']
},
{
fields: ['publish_time']
},
{
fields: ['author']
}
],
classMethods: {
findByNameAndVersion: function* (name, version) {
return yield this.find({
where: { name: name, version: version }
});
}
}
});
};

58
models/module_deps.js Normal file
View File

@@ -0,0 +1,58 @@
/**!
* cnpmjs.org - models/module_deps.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
/*
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',
`deps` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '`name` is deped by `deps`',
PRIMARY KEY (`id`),
UNIQUE KEY `module_deps_name_deps` (`name`,`deps`),
KEY `deps` (`module_deps_deps`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module deps';
*/
module.exports = function (sequelize, DataTypes) {
return sequelize.define('ModuleDependency', {
name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'module name',
},
dependent: {
field: 'deps',
type: DataTypes.STRING(100),
comment: '`name` is depended by `deps`. `deps` == depend => `name`'
}
}, {
tableName: 'module_deps',
comment: 'module deps',
// no need update timestamp
updatedAt: false,
indexes: [
{
unique: true,
fields: ['name', 'deps']
},
{
fields: ['deps']
}
],
classMethods: {
}
});
};

69
models/module_keyword.js Normal file
View File

@@ -0,0 +1,69 @@
/**!
* cnpmjs.org - models/module_keyword.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
/*
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',
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`description` longtext,
PRIMARY KEY (`id`),
UNIQUE KEY `keyword_module_name` (`keyword`,`name`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module keyword';
*/
module.exports = function (sequelize, DataTypes) {
return sequelize.define('ModuleKeyword', {
keyword: {
type: DataTypes.STRING(100),
allowNull: false,
},
name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'module name',
},
description: {
type: DataTypes.LONGTEXT,
allowNull: true,
}
}, {
tableName: 'module_keyword',
comment: 'module keyword',
updatedAt: false,
indexes: [
{
unique: true,
fields: ['keyword', 'name']
},
{
fields: ['name']
}
],
classMethods: {
findByKeywordAndName: function* (keyword, name) {
return yield this.find({
where: {
keyword: keyword,
name: name
}
});
}
}
});
};

56
models/module_log.js Normal file
View File

@@ -0,0 +1,56 @@
/**!
* cnpmjs.org - models/module_log.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
/*
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',
`username` varchar(100) NOT NULL,
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`log` longtext,
PRIMARY KEY (`id`),
KEY `module_log_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module sync log';
*/
module.exports = function (sequelize, DataTypes) {
return sequelize.define('ModuleLog', {
username: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'user name'
},
name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'module name',
},
log: {
type: DataTypes.LONGTEXT
}
}, {
tableName: 'module_log',
comment: 'module sync log',
indexes: [
{
fields: ['name']
}
],
classMethods: {
}
});
};

View File

@@ -0,0 +1,56 @@
/**!
* cnpmjs.org - models/module_maintainer.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
/*
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 `module_maintainer_user_name` (`user`,`name`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='private module maintainers';
*/
module.exports = function (sequelize, DataTypes) {
return sequelize.define('ModuleMaintainer', {
user: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'user name'
},
name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'module name',
}
}, {
tableName: 'module_maintainer',
comment: 'private module maintainers',
updatedAt: false,
indexes: [
{
unique: true,
fields: ['user', 'name']
},
{
fields: ['name']
}
],
classMethods: require('./_module_maintainer_class_methods'),
});
};

57
models/module_star.js Normal file
View File

@@ -0,0 +1,57 @@
/**!
* cnpmjs.org - models/module_star.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
/*
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',
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
PRIMARY KEY (`id`),
UNIQUE KEY `module_star_user_name` (`user`,`name`),
KEY `module_star_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module star';
*/
module.exports = function (sequelize, DataTypes) {
return sequelize.define('ModuleStar', {
user: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'user name'
},
name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'module name',
}
}, {
tableName: 'module_star',
comment: 'module star',
updatedAt: false,
indexes: [
{
unique: true,
fields: ['user', 'name']
},
{
fields: ['name']
}
],
classMethods: {
}
});
};

View File

@@ -0,0 +1,84 @@
/**!
* cnpmjs.org - models/module_unpublished.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var utils = require('./utils');
/*
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 `module_unpublished_name` (`name`),
KEY `module_unpublished_gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module unpublished info';
*/
module.exports = function (sequelize, DataTypes) {
return sequelize.define('ModuleUnpublished', {
name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'module name',
},
package: {
type: DataTypes.LONGTEXT,
comment: 'base info: tags, time, maintainers, description, versions',
get: utils.JSONGetter('package'),
set: utils.JSONSetter('package'),
}
}, {
tableName: 'module_unpublished',
comment: 'module unpublished info',
indexes: [
{
unique: true,
fields: ['name']
},
{
fields: ['gmt_modified']
}
],
classMethods: {
findByName: function* (name) {
return yield this.find({
where: {
name: name
}
});
},
save: function* (name, pkg) {
var row = yield this.find({
where: {
name: name
}
});
if (row) {
row.package = pkg;
return yield row.save(['package']);
}
row = this.build({
name: name,
package: pkg,
});
return yield row.save();
}
}
});
};

View File

@@ -0,0 +1,56 @@
/**!
* cnpmjs.org - models/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.
*/
/*
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 `npm_module_maintainer_user_name` (`user`,`name`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='npm original module maintainers';
*/
module.exports = function (sequelize, DataTypes) {
return sequelize.define('NpmModuleMaintainer', {
user: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'user name'
},
name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'module name',
}
}, {
tableName: 'npm_module_maintainer',
comment: 'npm original module maintainers',
updatedAt: false,
indexes: [
{
unique: true,
fields: ['user', 'name']
},
{
fields: ['name']
}
],
classMethods: require('./_module_maintainer_class_methods'),
});
};

72
models/tag.js Normal file
View File

@@ -0,0 +1,72 @@
/**!
* cnpmjs.org - models/tag.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
/*
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',
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`tag` varchar(30) NOT NULL COMMENT 'tag name',
`version` varchar(30) NOT NULL COMMENT 'module version',
`module_id` bigint(20) unsigned NOT NULL COMMENT 'module id',
PRIMARY KEY (`id`),
UNIQUE KEY `tag_name_tag` (`name`, `tag`),
KEY `tag_gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module tag';
*/
module.exports = function (sequelize, DataTypes) {
return sequelize.define('Tag', {
name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'module name',
},
tag: {
type: DataTypes.STRING(30),
allowNull: false,
comment: 'tag name',
},
version: {
type: DataTypes.STRING(30),
allowNull: false,
comment: 'module version',
},
module_id: {
type: DataTypes.BIGINT(20).UNSIGNED,
allowNull: false,
comment: 'module id'
}
}, {
tableName: 'tag',
comment: 'module tag',
indexes: [
{
unique: true,
fields: ['name', 'tag']
},
{
fields: ['gmt_modified']
}
],
classMethods: {
findByNameAndTag: function* (name, tag) {
return yield this.find({ where: { name: name, tag: tag } });
}
}
});
};

118
models/total.js Normal file
View File

@@ -0,0 +1,118 @@
/**!
* cnpmjs.org - models/total.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
// 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',
// `last_sync_time` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'last timestamp sync from official registry',
// `last_exist_sync_time` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'last timestamp sync exist packages from official registry',
// `sync_status` tinyint unsigned NOT NULL DEFAULT '0' COMMENT 'system sync from official registry status',
// `need_sync_num` int unsigned NOT NULL DEFAULT '0' COMMENT 'how many packages need to be sync',
// `success_sync_num` int unsigned NOT NULL DEFAULT '0' COMMENT 'how many packages sync success at this time',
// `fail_sync_num` int unsigned NOT NULL DEFAULT '0' COMMENT 'how many packages sync fail at this time',
// `left_sync_num` int unsigned NOT NULL DEFAULT '0' COMMENT 'how many packages left to be sync',
// `last_sync_module` varchar(100) COMMENT 'last sync success module name',
// PRIMARY KEY (`name`)
// ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='total info';
// -- init `total` count
// INSERT INTO total(name, gmt_modified) VALUES('total', now())
// ON DUPLICATE KEY UPDATE gmt_modified=now();
module.exports = function (sequelize, DataTypes) {
return sequelize.define('Total', {
name: {
type: DataTypes.STRING(100),
primaryKey: true,
comment: 'total name'
},
module_delete: {
type: DataTypes.BIGINT(20).UNSIGNED,
allowNull: false,
defaultValue: 0,
comment: 'module delete count',
},
last_sync_time: {
type: DataTypes.BIGINT(20).UNSIGNED,
allowNull: false,
defaultValue: 0,
comment: 'last timestamp sync from official registry',
},
last_exist_sync_time: {
type: DataTypes.BIGINT(20).UNSIGNED,
allowNull: false,
defaultValue: 0,
comment: 'last timestamp sync exist packages from official registry',
},
sync_status: {
type: 'TINYINT',
allowNull: false,
defaultValue: 0,
comment: 'system sync from official registry status',
},
need_sync_num: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
defaultValue: 0,
comment: 'how many packages need to be sync',
},
success_sync_num: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
defaultValue: 0,
comment: 'how many packages sync success at this time',
},
fail_sync_num: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
defaultValue: 0,
comment: 'how many packages sync fail at this time',
},
left_sync_num: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
defaultValue: 0,
comment: 'how many packages left to be sync',
},
last_sync_module: {
type: DataTypes.STRING(100),
allowNull: true,
comment: 'last sync success module name',
},
}, {
tableName: 'total',
comment: 'total info',
createdAt: false,
classMethods: {
init: function (callback) {
var that = this;
that.find({
where: { name: 'total' }
}).then(function (row) {
if (!row) {
that.build({name: 'total'}).save()
.then(function () {
callback();
})
.catch(callback);
return;
}
callback();
}).catch(callback);
}
}
});
};

241
models/user.js Normal file
View File

@@ -0,0 +1,241 @@
/**!
* cnpmjs.org - models/user.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var utility = require('utility');
var utils = require('./utils');
/*
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',
`name` varchar(100) NOT NULL COMMENT 'user name',
`salt` varchar(100) NOT NULL,
`password_sha` varchar(100) NOT NULL COMMENT 'user password hash',
`ip` varchar(64) NOT NULL COMMENT 'user last request ip',
`roles` varchar(200) NOT NULL DEFAULT '[]',
`rev` varchar(40) NOT NULL,
`email` varchar(400) NOT NULL,
`json` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'json details',
`npm_user` tinyint(1) DEFAULT '0' COMMENT 'user sync from npm or not, 1: true, other: false',
PRIMARY KEY (`id`),
UNIQUE KEY `user_name` (`name`),
KEY `user_gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='user base info';
*/
module.exports = function (sequelize, DataTypes) {
return sequelize.define('User', {
name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'user name',
},
salt: {
type: DataTypes.STRING(100),
allowNull: false,
},
password_sha: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'user password hash',
},
ip: {
type: DataTypes.STRING(64),
allowNull: false,
comment: 'user last request ip',
},
roles: {
type: DataTypes.STRING(200),
allowNull: false,
defaultValue: '[]',
},
rev: {
type: DataTypes.STRING(40),
allowNull: false,
},
email: {
type: DataTypes.STRING(400),
allowNull: false,
},
json: {
type: DataTypes.LONGTEXT,
allowNull: true,
get: utils.JSONGetter('json'),
set: utils.JSONSetter('json'),
},
isNpmUser: {
field: 'npm_user',
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false,
comment: 'user sync from npm or not, 1: true, other: false',
}
}, {
tableName: 'user',
comment: 'user base info',
indexes: [
{
unique: true,
fields: ['name']
},
{
fields: ['gmt_modified']
}
],
classMethods: {
// utils
createPasswordSha: function (password, salt) {
return utility.sha1(password + salt);
},
// read
auth: function* (name, password) {
var user = yield* this.findByName(name);
if (user) {
var sha = this.createPasswordSha(password, user.salt);
if (user.password_sha !== sha) {
user = null;
}
}
return user;
},
findByName: function* (name) {
return yield this.find({ where: { name: name } });
},
listByNames: function* (names) {
return yield this.findAll({
where: {
name: {
in: names
}
}
});
},
search: function* (query, options) {
return yield this.findAll({
where: {
name: {
like: query + '%'
}
},
limit: options.limit
});
},
// write
saveNpmUser: function* (data) {
var user = yield* this.findByName(data.name);
if (!user) {
user = this.build({
isNpmUser: true,
name: data.name,
salt: '0',
password_sha: '0',
ip: '0',
});
}
user.isNpmUser = true;
user.json = data;
user.email = data.email || '';
user.rev = data._rev || '';
return yield user.save();
},
saveCustomUser: function* (data) {
var name = data.user.login;
var user = yield* this.findByName(name);
if (!user) {
user = this.build({
isNpmUser: false,
name: name,
});
}
var rev = '1-' + data.user.login;
var salt = data.salt || '0';
var passwordSha = data.password_sha || '0';
var ip = data.ip || '0';
user.isNpmUser = false;
user.email = data.user.email;
user.ip = ip;
user.json = data.user;
user.rev = rev;
user.salt = salt;
user.password_sha = passwordSha;
return yield user.save();
},
// add cnpm user
add: function* (user) {
var roles = user.roles || [];
try {
roles = JSON.stringify(roles);
} catch (e) {
roles = '[]';
}
var rev = '1-' + utility.md5(JSON.stringify(user));
var row = this.build({
rev: rev,
name: user.name,
email: user.email,
salt: user.salt,
password_sha: user.password_sha,
ip: user.ip,
roles: roles,
isNpmUser: false,
});
return yield row.save();
},
update: function* (user) {
var rev = user.rev || user._rev;
var revNo = Number(rev.split('-', 1));
if (!revNo) {
var err = new Error(rev + ' format error');
err.name = 'RevFormatError';
err.data = {user: user};
throw err;
}
revNo++;
var newRev = revNo + '-' + utility.md5(JSON.stringify(user));
var roles = user.roles || [];
try {
roles = JSON.stringify(roles);
} catch (e) {
roles = '[]';
}
var row = yield* this.findByName(user.name);
if (!row) {
return null;
}
row.rev = newRev;
row.email = user.email;
row.salt = user.salt;
row.password_sha = user.password_sha;
row.ip = user.ip;
row.roles = roles;
row.isNpmUser = false;
return yield row.save(['rev', 'email', 'salt', 'password_sha', 'ip', 'roles', 'isNpmUser']);
}
}
});
};

34
models/utils.js Normal file
View File

@@ -0,0 +1,34 @@
/**!
* cnpmjs.org - models/utils.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
exports.JSONGetter = function (propertyName) {
return function JSONGetter() {
var value = this.getDataValue(propertyName);
if (value && typeof value === 'string') {
value = JSON.parse(value);
}
return value;
};
};
exports.JSONSetter = function (propertyName) {
return function JSONSetter(value) {
if (typeof value !== 'string') {
value = JSON.stringify(value);
}
this.setDataValue(propertyName, value);
};
};

View File

@@ -1,69 +1,67 @@
{
"name": "cnpmjs.org",
"version": "0.2.24",
"version": "2.0.0-beta0",
"description": "Private npm registry and web for Enterprise, base on MySQL and Simple Store Service",
"main": "index.js",
"scripts": {
"test": "make test-all",
"test": "make install && make jshint && make test",
"start": "./bin/nodejsctl start && cp History.md docs/web/history.md",
"status": "./bin/nodejsctl status",
"stop": "./bin/nodejsctl stop"
},
"config": {
"blanket": {
"pattern": "//^((?!(node_modules|test|common)).)*$/",
"data-cover-flags": {
"debug": false
}
},
"travis-cov": {
"threshold": 90
}
},
"dependencies": {
"bagpipe": "0.3.5",
"connect": "2.12.0",
"connect-markdown": "0.0.3",
"connect-redis": "1.4.6",
"connect-render": "0.3.2",
"connect-rt": "0.0.2",
"debug": "0.7.4",
"eventproxy": "0.2.6",
"forward": "0.0.4",
"giturl": "0.0.1",
"graceful": "0.0.5",
"gravatar": "1.0.6",
"humanize-number": "0.0.2",
"logfilestream": "0.1.0",
"marked": "0.3.0",
"microtime": "0.5.1",
"mime": "1.2.11",
"mkdirp": "0.3.5",
"moment": "2.5.1",
"ms": "0.6.2",
"mysql": "2.0.1",
"nodemailer": "0.6.0",
"qn": "0.2.0",
"ready": "0.1.1",
"response-cookie": "0.0.2",
"response-patch": "0.1.1",
"semver": "2.2.1",
"urllib": "0.5.5",
"urlrouter": "0.5.4",
"utility": "0.1.10"
"agentkeepalive": "~1.2.0",
"bluebird": "~2.3.10",
"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-redis": "~1.1.0",
"co-sleep": "~0.0.1",
"copy-to": "~2.0.1",
"debug": "~2.1.0",
"error-formater": "~1.0.3",
"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.13.0",
"koa-limit": "~1.0.2",
"koa-markdown": "~1.0.0",
"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",
"mysql": "~2.5.2",
"nodemailer": "~1.3.0",
"redis": "~0.12.1",
"semver": "~4.1.0",
"sequelize": "~2.0.0-rc2",
"thunkify-wrap": "~1.0.2",
"urllib": "~2.0.1",
"utility": "~1.2.0"
},
"devDependencies": {
"autod": ">=0.0.13",
"blanket": "*",
"autod": "*",
"chunkstream": "*",
"co-mocha": "*",
"contributors": "*",
"coveralls": "*",
"mm": "0.1.8",
"istanbul-harmony": "*",
"jshint": "*",
"mm": "*",
"mocha": "*",
"mocha-lcov-reporter": "*",
"pedding": "0.0.3",
"should": "3.0.1",
"supertest": "0.9.0",
"travis-cov": "*"
"node-dev": "*",
"pedding": "*",
"should": "~4.0.4",
"should-http": "*",
"sqlite3": "*",
"supertest": "*"
},
"homepage": "https://github.com/cnpm/cnpmjs.org",
"repository": {
@@ -76,10 +74,14 @@
"email": "fengmk2@gmail.com"
},
"keywords": [
"cnpmjs.org", "npm", "npmjs", "npmjs.org", "registry"
"cnpmjs.org",
"npm",
"npmjs",
"npmjs.org",
"registry"
],
"engines": {
"node": ">= 0.8.0"
"node": ">= 0.11.14"
},
"author": [
"fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)",

View File

@@ -1,43 +0,0 @@
/**!
* cnpmjs.org - proxy/download.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var config = require('../config');
var mysql = require('../common/mysql');
var PLUS_SQL = 'INSERT INTO download_total(gmt_create, gmt_modified, \
date, name, count) \
VALUES(now(), now(), ?, ?, ?) \
ON DUPLICATE KEY UPDATE gmt_modified=now(), \
count=count + VALUES(count), name=VALUES(name), date=VALUES(date);';
exports.plusTotal = function (data, callback) {
mysql.query(PLUS_SQL, [data.date, data.name, data.count], callback);
};
var SELECT_ONE_TOTAL_SQL = 'SELECT date, count FROM download_total WHERE date>=? AND date<=? AND name=?;';
exports.getModuleTotal = function (name, start, end, callback) {
mysql.query(SELECT_ONE_TOTAL_SQL, [start, end, name], callback);
};
var SELECT_ALL_TOTAL_SQL = 'SELECT date, sum(count) AS count \
FROM download_total \
WHERE date>=? AND date<=? \
GROUP BY date;';
exports.getTotal = function (start, end, callback) {
mysql.query(SELECT_ALL_TOTAL_SQL, [start, end], callback);
};

View File

@@ -1,450 +0,0 @@
/**!
* cnpmjs.org - proxy/module.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var utility = require('utility');
var eventproxy = require('eventproxy');
var config = require('../config');
var mysql = require('../common/mysql');
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 = 'INSERT INTO module(gmt_create, gmt_modified, \
publish_time, author, name, version, package, dist_tarball, dist_shasum, dist_size, description) \
VALUES(now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?) \
ON DUPLICATE KEY UPDATE gmt_modified=now(), publish_time=VALUES(publish_time), description=VALUES(description), \
author=VALUES(author), name=VALUES(name), version=VALUES(version), package=VALUES(package), \
dist_tarball=VALUES(dist_tarball), dist_shasum=VALUES(dist_shasum), dist_size=VALUES(dist_size);';
exports.add = function (mod, callback) {
var keywords = mod.package.keywords;
var pkg;
try {
pkg = stringifyPackage(mod.package);
} catch (e) {
return callback(e);
}
var description = mod.package && mod.package.description || '';
var dist = mod.package.dist || {};
dist.tarball = '';
dist.shasum = '';
dist.size = 0;
var publish_time = mod.publish_time || Date.now();
var values = [
publish_time, mod.author, mod.name, mod.version, pkg,
dist.tarball, dist.shasum, dist.size, description
];
mysql.query(INSERT_MODULE_SQL, values, function (err, result) {
if (err) {
return callback(err);
}
callback(null, {id: result.insertId, gmt_modified: new Date()});
if (typeof keywords === 'string') {
keywords = [keywords];
}
if (!Array.isArray(keywords)) {
return;
}
var words = [];
for (var i = 0; i < keywords.length; i++) {
var w = keywords[i];
if (typeof w === 'string') {
w = w.trim();
if (w) {
words.push(w);
}
}
}
if (words.length === 0) {
return;
}
// add keywords
exports.addKeywords(mod, description, words, utility.noop);
});
};
var GET_KEYWORD_SQL = 'SELECT keyword FROM module_keyword WHERE name=? ORDER BY keyword;';
exports.getKeywords = function (name, callback) {
mysql.query(GET_KEYWORD_SQL, [name], function (err, rows) {
var keywords = [];
if (rows && rows.length) {
keywords = rows.map(function (r) {
return r.keyword;
});
}
callback(err, keywords);
});
};
var ADD_KEYWORD_SQL = 'INSERT INTO module_keyword(gmt_create, keyword, name, description) \
VALUES(now(), ?, ?, ?) \
ON DUPLICATE KEY UPDATE description=VALUES(description);';
exports.addKeywords = function (name, description, keywords, callback) {
var sql = '';
var values = [];
for (var i = 0; i < keywords.length; i++) {
sql += ADD_KEYWORD_SQL;
values.push(keywords[i]);
values.push(name);
values.push(description);
}
mysql.query(sql, values, function (err, results) {
if (err) {
return callback(err);
}
var ids = [];
for (var i = 0; i < results.length; i++) {
var r = results[i];
if (r.insertId) {
ids.push(r.insertId);
}
}
callback(null, ids);
});
};
var UPDATE_DESC_SQL = 'UPDATE module SET description=? WHERE id=?;';
exports.updateDescription = function (id, description, callback) {
mysql.query(UPDATE_DESC_SQL, [description, id], callback);
};
var UPDATE_PACKAGE_SQL = 'UPDATE module SET package=? WHERE id=?;';
exports.updateReadme = function (id, readme, callback) {
exports.getById(id, function (err, data) {
if (err) {
return callback(err);
}
data.package = data.package || {};
data.package.readme = readme;
var pkg = stringifyPackage(data.package);
mysql.query(UPDATE_PACKAGE_SQL, [pkg, id], callback);
});
};
var UPDATE_DIST_SQL = 'UPDATE module SET publish_time=?, version=?, package=?, \
dist_tarball=?, dist_shasum=?, dist_size=? WHERE id=?;';
exports.update = function (mod, callback) {
var pkg;
try {
pkg = stringifyPackage(mod.package);
} catch (e) {
return callback(e);
}
var dist = mod.package.dist;
mysql.query(UPDATE_DIST_SQL,
[mod.publish_time, mod.version, pkg, dist.tarball, dist.shasum, dist.size, mod.id],
function (err, result) {
if (err) {
return callback(err);
}
callback(null, {id: mod.id, gmt_modified: new Date()});
});
};
function parseRow(row) {
if (row && row.package) {
try {
if (row.package.indexOf('%7B%22') === 0) {
// now store package will encodeURIComponent() after JSON.stringify
row.package = decodeURIComponent(row.package);
}
row.package = JSON.parse(row.package);
} catch (e) {
console.warn('parse package error: %s, id: %s version: %s, error: %s', row.name, row.id, row.version, e);
}
}
}
function stringifyPackage(pkg) {
return encodeURIComponent(JSON.stringify(pkg));
}
var SELECT_MODULE_BY_ID_SQL = 'SELECT ' + MODULE_COLUMNS + ' FROM module WHERE id=?;';
exports.getById = function (id, callback) {
id = Number(id);
mysql.queryOne(SELECT_MODULE_BY_ID_SQL, [id], function (err, row) {
if (err || !row) {
return callback(err, row);
}
try {
parseRow(row);
} catch (e) {
e.data = row;
return callback(e);
}
callback(null, row);
});
};
var SELECT_MODULE_SQL = 'SELECT ' + MODULE_COLUMNS + ' FROM module WHERE name=? AND version=?;';
exports.get = function (name, version, callback) {
mysql.queryOne(SELECT_MODULE_SQL, [name, version], function (err, row) {
if (err || !row) {
return callback(err, row);
}
try {
parseRow(row);
} catch (e) {
e.data = row;
return callback(e);
}
callback(null, row);
});
};
var INSERT_TAG_SQL = 'INSERT INTO tag(gmt_create, gmt_modified, \
name, tag, version, module_id) \
VALUES(now(), now(), ?, ?, ?, ?) \
ON DUPLICATE KEY UPDATE gmt_modified=now(), module_id=VALUES(module_id), \
name=VALUES(name), tag=VALUES(tag), version=VALUES(version);';
var SELECT_MODULE_ID_SQL = 'SELECT id FROM module WHERE name=? AND version=?;';
exports.addTag = function (name, tag, version, callback) {
mysql.queryOne(SELECT_MODULE_ID_SQL, [name, version], function (err, row) {
if (err) {
return callback(err);
}
var module_id = row && row.id || 0;
mysql.query(INSERT_TAG_SQL, [name, tag, version, module_id], function (err, result) {
if (err) {
return callback(err);
}
callback(null, {id: result.insertId, gmt_modified: new Date(), module_id: module_id});
});
});
};
var SELECT_TAG_SQL = 'SELECT tag, version, gmt_modified, module_id FROM tag WHERE name=? AND tag=?;';
exports.getByTag = function (name, tag, callback) {
mysql.queryOne(SELECT_TAG_SQL, [name, tag], function (err, row) {
if (err || !row) {
return callback(err, row);
}
exports.get(name, row.version, callback);
});
};
var DELETE_TAGS_SQL = 'DELETE FROM tag WHERE name=?;';
exports.removeTags = function (name, callback) {
mysql.query(DELETE_TAGS_SQL, [name], callback);
};
var DELETE_TAGS_BY_IDS_SQL = 'DELETE FROM tag WHERE id in (?)';
exports.removeTagsByIds = function (ids, callback) {
mysql.query(DELETE_TAGS_BY_IDS_SQL, [ids], callback);
};
var SELECT_ALL_TAGS_SQL = 'SELECT id, tag, version, gmt_modified, module_id FROM tag WHERE name=?;';
exports.listTags = function (name, callback) {
mysql.query(SELECT_ALL_TAGS_SQL, [name], callback);
};
var SELECT_LATEST_MODULE_SQL = 'SELECT ' + MODULE_COLUMNS +
' FROM module WHERE name=? AND version <> "next" ORDER BY publish_time DESC LIMIT 1;';
exports.getLatest = function (name, callback) {
exports.getByTag(name, 'latest', function (err, row) {
if (err || row) {
return callback(err, row);
}
// get latest order by id
mysql.queryOne(SELECT_LATEST_MODULE_SQL, [name], function (err, row) {
if (err || !row) {
return callback(err, row);
}
try {
parseRow(row);
} catch (e) {
e.data = row;
return callback(e);
}
callback(null, row);
});
});
};
var LIST_MODULE_SQL = 'SELECT ' + MODULE_COLUMNS + ' FROM module WHERE name=? ORDER BY id DESC;';
exports.listByName = function (name, callback) {
mysql.query(LIST_MODULE_SQL, [name], function (err, rows) {
if (err) {
return callback(err);
}
rows = rows || [];
try {
for (var i = 0; i < rows.length; i++) {
parseRow(rows[i]);
}
} catch (e) {
err = e;
err.data = rows;
}
callback(err, rows);
});
};
var LIST_SINCE_SQLS = [
'SELECT module_id FROM tag WHERE tag="latest" AND gmt_modified>?',
'SELECT name, package FROM module WHERE id IN (?);'
];
exports.listSince = function (start, callback) {
var ep = eventproxy.create();
ep.fail(callback);
mysql.query(LIST_SINCE_SQLS[0], [new Date(start)], ep.done(function (rows) {
if (!rows || rows.length === 0) {
return callback(null, []);
}
ep.emit('ids', rows.map(function (r) {
return r.module_id;
}));
}));
ep.once('ids', function (ids) {
mysql.query(LIST_SINCE_SQLS[1], [ids], callback);
});
};
var LIST_SHORT_SQL = 'SELECT distinct(name) FROM tag ORDER BY name';
exports.listShort = function (callback) {
mysql.query(LIST_SHORT_SQL, callback);
};
var LIST_ALL_MODULE_NAMES_SQL = 'SELECT distinct(name) FROM module ORDER BY name';
exports.listAllModuleNames = function (callback) {
mysql.query(LIST_ALL_MODULE_NAMES_SQL, callback);
};
var DELETE_MODULE_BY_NAME_SQL = 'DELETE FROM module WHERE name=?;';
exports.removeByName = function (name, callback) {
mysql.query(DELETE_MODULE_BY_NAME_SQL, [name], callback);
};
var DELETE_MODULE_BY_NAME_AND_VERSIONS_SQL = 'DELETE FROM module WHERE name=? AND version IN(?);';
exports.removeByNameAndVersions = function (name, versions, callback) {
mysql.query(DELETE_MODULE_BY_NAME_AND_VERSIONS_SQL, [name, versions], callback);
};
var LIST_BY_AUTH_SQLS = [
'SELECT distinct(name) AS name FROM module WHERE author = ? ORDER BY publish_time DESC LIMIT 100;',
'SELECT module_id FROM tag WHERE tag="latest" AND name IN (?)',
'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 = 'SELECT module_id FROM tag WHERE name LIKE ? AND tag="latest" ORDER BY name LIMIT ?;';
var SEARCH_MODULES_BY_KEYWORD_SQL = 'SELECT name, description FROM module_keyword WHERE keyword = ? ORDER BY id DESC LIMIT ?;';
var QUERY_MODULES_BY_ID_SQL = 'SELECT name, description FROM module WHERE id IN (?) ORDER BY name;';
exports.search = function (word, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
options = options || {};
var limit = options.limit || 100;
word = word.replace(/^%/, ''); //ignore prefix %
var ep = eventproxy.create();
ep.fail(callback);
// search flows:
// 1. prefix search by name
// 2. like search by name
// 3. keyword equal search
var ids = {};
mysql.query(SEARCH_MODULES_SQL, [word + '%', limit ], ep.done(function (rows) {
rows = rows || [];
if (rows.length > 0) {
for (var i = 0; i < rows.length; i++) {
ids[rows[i].module_id] = 1;
}
}
if (rows.length >= 20) {
return ep.emit('ids', Object.keys(ids));
}
mysql.query(SEARCH_MODULES_SQL, [ '%' + word + '%', limit ], ep.done('likeSearch'));
}));
mysql.query(SEARCH_MODULES_BY_KEYWORD_SQL, [ word, limit ], ep.done('keywordRows'));
ep.on('likeSearch', function (rows) {
rows = rows || [];
if (rows.length > 0) {
for (var i = 0; i < rows.length; i++) {
ids[rows[i].module_id] = 1;
}
}
ep.emit('ids', Object.keys(ids));
});
ep.all('ids', 'keywordRows', function (ids, keywordRows) {
keywordRows = keywordRows || [];
var data = {
keywordMatchs: keywordRows,
searchMatchs: []
};
if (ids.length === 0) {
return callback(null, data);
}
mysql.query(QUERY_MODULES_BY_ID_SQL, [ids], ep.done(function (modules) {
data.searchMatchs = modules;
callback(null, data);
}));
});
};

View File

@@ -1,42 +0,0 @@
/**!
* cnpmjs.org - proxy/module_log.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var mysql = require('../common/mysql');
var INSERT_LOG_SQL = 'INSERT INTO module_log(gmt_create, gmt_modified, name, username, log) \
VALUES(now(), now(), ?, ?, "");';
exports.create = function (data, callback) {
mysql.query(INSERT_LOG_SQL, [data.name, data.username], function (err, result) {
if (err) {
return callback(err);
}
callback(null, {id: result.insertId, gmt_modified: new Date()});
});
};
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) {
callback(null, {id: id, gmt_modified: new Date()});
});
};
var SELECT_SQL = 'SELECT * FROM module_log WHERE id=?;';
exports.get = function (id, callback) {
mysql.queryOne(SELECT_SQL, [id], callback);
};

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