Compare commits

...

601 Commits

Author SHA1 Message Date
天玎
1a67afac72 [WIP] fix: version length validation 2021-07-16 13:39:14 +08:00
Solais
e2582417fa remove maintainers logic (#1654)
* fix: remove maintainers logic when token mode

Co-authored-by: zhixiang.tzx <zhixiang.tzx@alibaba-inc.com>
2021-07-06 17:52:43 +08:00
killagu
99b81f531e Release 3.0.0-rc.44 2021-06-11 10:38:11 +08:00
killa
a1e8a82892 feat: impl npm owner hooks (#1645)
* feat: impl npm owner hooks
* fix: fix tagDir/packageDir for oss-cnpm
2021-06-11 10:34:50 +08:00
killagu
14a6f621f8 Release 3.0.0-rc.43 2021-05-06 20:59:25 +08:00
killa
f1217094b7 fix: should yield backup method (#1639) 2021-05-06 15:02:57 +08:00
killa
2245dc2967 feat: impl accelerate request (#1637)
* feat: impl accelerate request
2021-05-06 15:00:04 +08:00
killagu
3d34058542 Release 3.0.0-rc.42 2021-04-30 14:40:01 +08:00
killa
a21aed08c5 feat: impl sync to/from backup files (#1636)
* feat: impl save backup files

* feat: impl sync from backup files

* feat: impl sync unpublish from backup file

* config: add backupProtocol
2021-04-30 11:48:11 +08:00
fengmk2
cb2f3de5a6 Release 3.0.0-rc.41 2021-04-19 22:55:52 +08:00
fengmk2
819a499661 feat: add custom format hooks for registry package request (#1635)
let custom registry can filter the package fields itself
2021-04-19 22:54:51 +08:00
fengmk2
dc0955320b feat: export models ref to app (#1634)
let custom middleware can access app.models
2021-04-18 13:55:02 +08:00
fengmk2
aa7cb3df01 feat: allow to disable total modules query on db (#1624)
use `config.enableTotalCount = false`
2021-02-24 18:29:49 +08:00
fengmk2
35d9f9b00e Release 3.0.0-rc.40 2021-02-05 21:45:07 +08:00
Bowen Huang
8ddbe9076f feat: custom header html block (#1616)
Co-authored-by: yanmei.hbw <yanmei.hbw@alibaba-inc.com>
2021-02-05 21:43:40 +08:00
withoutmeat
f0ba573f8b fix: wrong position of the comma in the db.sql (#1613) 2021-01-19 12:23:04 +08:00
killagu
134839d31e Release 3.0.0-rc.39 2021-01-14 10:22:03 +08:00
killa
33dd3554f5 feat: impl dist-tag hooks (#1612)
* feat: impl dist-tag hooks

* test: add tag assert
2021-01-14 10:20:16 +08:00
killagu
dffe589e2c Release 3.0.0-rc.38 2021-01-12 22:05:25 +08:00
killa
1c6040bc95 performance: optimise query pkg latestModified (#1611) 2021-01-12 21:54:38 +08:00
Li Shuai
7909434b86 chore: add alibaba cloud devops link (#1602)
Co-authored-by: 焦霸 <jiaoba@taobao.com>
2020-11-24 19:56:48 +08:00
dead-horse
09ec3f5392 Release 3.0.0-rc.37 2020-10-21 13:57:41 +08:00
killa
39c322332f feat: new registry api (#1597) 2020-10-21 13:53:44 +08:00
fengmk2
24a0039188 Release 3.0.0-rc.36 2020-09-28 20:18:10 +08:00
killa
b7089d33d4 fix: set maintainer to current user if maintainer is undefined (#1592)
If pub with token, pkg has no default maintainer
2020-09-28 20:16:15 +08:00
fengmk2
2b74e00cb9 fix: release 3.0.0-rc.35 fix npm include functions dir 2020-09-20 21:50:43 +08:00
fengmk2
d26e9fdff1 Release 3.0.0-rc.34 2020-09-20 21:46:32 +08:00
killa
45f2f8b31f feat: impl registry token api (#1590)
Refs:
- https://github.com/npm/registry/blob/master/docs/user/authentication.md
2020-09-20 21:41:15 +08:00
fengmk2
90d39aa4fc Release 3.0.0-rc.33 2020-08-07 11:27:39 +08:00
fengmk2
61549b47a2 fix: avoid "ENAMETOOLONG: name too long" error (#1583) 2020-08-07 11:26:23 +08:00
fengmk2
5e9859119f Release 3.0.0-rc.32 2020-04-10 10:56:17 +08:00
fengmk2
e97835f702 feat: support custom web middlewares (#1563) 2020-04-10 10:55:26 +08:00
fengmk2
3cb3fe02f0 feat: list all package versions by date (#1557) 2020-02-23 20:43:24 +08:00
fengmk2
2cea5cd1c3 Release 3.0.0-rc.31 2020-01-13 18:00:56 +08:00
fengmk2
a8ff647aa0 feat: retry sync fail on cnpm registry (#1547) 2020-01-13 18:00:09 +08:00
fengmk2
f48f27aae4 Release 3.0.0-rc.30 2019-12-12 16:26:23 +08:00
Khaidi Chu
2c511f2209 feat: add unpublishRemoveTarball mode (#1536) 2019-12-12 16:24:33 +08:00
Khaidi Chu
e7bafb2ee9 fix: audit proxy test cases (#1537) 2019-12-12 15:26:05 +08:00
alsotang
522ad11124 update readme for now situation (#1506) 2019-09-04 20:38:37 +08:00
fengmk2
6be1a198c2 Release 3.0.0-rc.29 2019-08-24 14:06:38 +08:00
fengmk2
19563f5851 feat: allow to disable npm audits proxy (#1430) 2019-08-24 14:05:40 +08:00
fengmk2
d5ba93ac0d Release 3.0.0-rc.28 2019-08-21 16:09:24 +08:00
Khaidi Chu
92b72169a8 fix: maintainer permission greater than scope (#1494) 2019-08-21 16:01:44 +08:00
fengmk2
4540845046 Release 3.0.0-rc.27 2019-06-09 14:55:12 +08:00
fengmk2
8e2367ee16 feat: dont check db data on tgz download request (#1477) 2019-06-09 14:52:54 +08:00
fengmk2
96369037f9 Merge branch 'master' of github.com:cnpm/cnpmjs.org 2019-06-09 13:37:08 +08:00
fengmk2
9da2c494f3 Release 3.0.0-rc.26 2019-05-24 15:45:02 +08:00
fengmk2
be05886452 feat: add vary header on cdn 2019-05-24 15:44:47 +08:00
Yiman Liu
f084ebae21 fix: cpu usage 100% in node@6.x (#1470)
1.  cpu usage 100% almost all the time, and I found that it have been fixed in node@12
     just like: https://github.com/libuv/libuv/issues/2162
2. only prod-dependences needed
2019-05-24 12:50:29 +08:00
fengmk2
dba5c90114 Release 3.0.0-rc.25 2019-05-24 11:49:57 +08:00
fengmk2
ea46399265 feat: allow disable search page 2019-05-24 11:49:37 +08:00
fengmk2
dd19a606fb Release 3.0.0-rc.24 2019-05-24 11:01:35 +08:00
fengmk2
8d572169b7 fix: add cache on total 2019-05-24 11:01:10 +08:00
fengmk2
3ffe576916 Release 3.0.0-rc.23 2019-05-24 10:12:40 +08:00
fengmk2
585f55bbcc fix: download url pathname
https://r.cnpmjs.org/electron-to-chromium/download/electron-to-chromium-1.3.137.tgz?sync_timestamp=1558663387092&other_urls=https%3A%2F%2Fr.cnpmjs.org%2Felectron-to-chromium%2Fdownload%2Felectron-to-chromium-1.3.137.tgz
2019-05-24 10:11:31 +08:00
fengmk2
2cb43c3e33 Release 3.0.0-rc.22 2019-05-23 23:50:16 +08:00
fengmk2
581925db97 feat: support cache-control header on registry request (#1468) 2019-05-23 23:49:49 +08:00
fengmk2
c385f9d97f Release 3.0.0-rc.21 2019-04-30 20:12:58 +08:00
fengmk2
da2f9640b8 fix: dont override exists weburl 2019-04-30 20:12:40 +08:00
fengmk2
754c74ca61 Release 3.0.0-rc.20 2019-04-29 17:40:22 +08:00
fengmk2
b094f5692f fix: changes stream syncer without deps 2019-04-29 17:39:56 +08:00
fengmk2
0c59791e50 Release Release 3.0.0-rc.19 2019-04-24 00:17:17 +08:00
fengmk2
7f0c141ac2 feat: allow config request protocol 2019-04-24 00:15:43 +08:00
Paul Verest
79fb163a3b chore: README fix typo ( not to use plural for code ) (#1448) 2019-03-26 18:26:35 +08:00
fengmk2
c0ba12960e Release 3.0.0-rc.18 2019-03-13 15:50:35 +08:00
alsotang
be00b65573 refactor: add detail message to error and keep reason (#1445)
closes https://github.com/cnpm/cnpmjs.org/issues/1443
2019-03-13 15:44:55 +08:00
fengmk2
316e722a2e Release 3.0.0-rc.17 2019-02-27 11:16:00 +08:00
fengmk2
65bca46f3c fix: don't cache npm_service.cnpmjs.org request 2019-02-27 11:15:38 +08:00
fengmk2
35b3cef918 Release 3.0.0-rc.16 2019-02-27 00:43:07 +08:00
fengmk2
807187ebeb feat: add redis cache to import list all versions api perf (#1441) 2019-02-27 00:41:59 +08:00
fengmk2
72b5a97cde Release 3.0.0-rc.15 2019-02-21 16:14:36 +08:00
fengmk2
f9d4858862 fix: avoid toString as downloads count key (#1438)
fix sql error as below:

```
near 'toString() { [native code] }1, gmt_modified='2019-02-21 01:05:55' WHERE id = 185' at line 1; name: toString, count: function toString() { [native code] }1, date: 2019-02-21
```
2019-02-21 16:03:04 +08:00
fengmk2
f7e9670025 Release Release 3.0.0-rc.14 2019-02-19 22:02:22 +08:00
Khaidi Chu
99c4c3fe35 feat: support customized middlewares (#1436) 2019-02-19 22:01:33 +08:00
fengmk2
444b5d125c Release 3.0.0-rc.13 2019-01-22 23:05:47 +08:00
fengmk2
4b57c118a0 feat: can override tgz download options
speed up download from the wall
2019-01-22 22:51:13 +08:00
alsotang
d0c3f1b19e test: shouldjs change from getter to function call (#1420) 2019-01-22 21:08:55 +08:00
alsotang
b395c666be feat: proxy npm audit request (#1419)
add test for #1407
2019-01-22 21:07:27 +08:00
fengmk2
03d7215d6c Release 3.0.0-rc.12 2018-12-04 18:48:37 +08:00
fengmk2
8a2f744749 fix: don't update __all__ downloads every times (#1417) 2018-12-04 18:47:54 +08:00
fengmk2
e2fc613c98 Release 3.0.0-rc.11 2018-12-01 14:56:30 +08:00
Albert Zhang
9bdb695375 fix: proxy to source registry when package is public scoped with encoded path (#1415) 2018-12-01 14:55:35 +08:00
alsotang
8bd0a2d491 fix: swap compress middleware and notFound position (#1413) 2018-11-28 11:08:05 +08:00
fengmk2
769af76c73 Release 3.0.0-rc.10 2018-11-26 17:56:46 +08:00
Khaidi Chu
93d5def8ac fix: show package when non-semver version of semver tag (#1411)
* test: fix listen multiple times under Node.js 10
2018-11-26 17:55:24 +08:00
XingKai Zhang
6a8434e0ca fix: Don't display sync info when the sync mode is none (#1410) 2018-11-20 10:49:26 +08:00
fengmk2
a4a25f9e38 feat: use faster etag instead of koa-etag (#1409)
* fix: should set weak etag avoid nginx(<1.7.3) remove it
2018-11-20 01:56:42 +08:00
fengmk2
300e0e676a Release 3.0.0-rc.8 2018-11-07 14:22:27 +08:00
Khaidi Chu
4a3a851256 fix: use <%- instead of <%= in user profile page (#1404) 2018-11-07 14:21:51 +08:00
fengmk2
6226ad8050 Release 3.0.0-rc.7 2018-10-26 14:52:59 +08:00
Khaidi Chu
90580a72e5 feat: configurable view directory (#1400) 2018-10-26 14:52:22 +08:00
Ankur Kumar
3497bae2b9 fix: Obfuscate email address (#1391)
fixes #1373
2018-10-24 00:04:31 +08:00
fengmk2
2073127796 Release 3.0.0-rc.6 2018-10-23 00:51:55 +08:00
fengmk2
d889ebafbd deps: use agentkeepalive@4
drop support node 6
2018-10-23 00:50:41 +08:00
fengmk2
ad2d341d2c feat: sync downloads total <= 10000 unpublish package
and don't show packagephobia if downloads < 1000
2018-10-23 00:50:41 +08:00
fengmk2
688da2d94b Release 3.0.0-rc.5 2018-09-26 20:03:07 +08:00
fengmk2
25a9030047 feat: allow to close mysql trace 2018-09-26 20:02:09 +08:00
Gwenael Pluchon
938a14d0a1 chore: Hall of Fame integration on README (#1388) 2018-09-19 17:44:52 +08:00
fengmk2
3be0a3261e Release 3.0.0-rc.4 2018-09-14 19:59:32 +08:00
fengmk2
017af69cce feat: add badgeService define on config (#1387)
```js
badgeService: {
  url(subject, status, options): return string
}
```
2018-09-14 19:54:30 +08:00
fengmk2
842c0316ed feat: show versions list on package page (#1386) 2018-09-14 18:46:15 +08:00
fengmk2
82d3ba777f Release 3.0.0-rc.3 2018-09-14 15:15:36 +08:00
fengmk2
9b8491b736 fix: use https://cdn.staticfile.org 2018-09-14 15:14:29 +08:00
fengmk2
364817481c Release 3.0.0-rc.2 2018-09-09 00:22:09 +08:00
fengmk2
fc799304d8 fix: should return [done] Sync {name} string when task finished (#1382)
the cnpm client sync need the `[done] Sync {name}` string to detect when sync task finished
2018-09-09 00:20:50 +08:00
fengmk2
68ef6983fb Release 3.0.0-rc.1 2018-09-08 13:13:18 +08:00
fengmk2
3c20267b22 fix: don't retry to save log when db error (#1381)
Only keep the last 50kb log on db.
2018-09-08 13:10:40 +08:00
Khaidi Chu
26d7147562 refactor: normalize database structure (#1376) 2018-09-05 02:04:11 +08:00
fengmk2
3141437a42 Release 3.0.0-beta.6 2018-08-25 02:56:23 +08:00
fengmk2
5149aa5a1e fix: proxy public package from source registry (#1375)
when syncMode = 'none'

closes https://github.com/cnpm/cnpmjs.org/issues/1374
2018-08-25 02:52:17 +08:00
fengmk2
bd87907b69 feat: auto retry if download tgz error (#1363)
closes https://github.com/cnpm/cnpmjs.org/issues/1362
2018-07-20 16:00:42 +08:00
fengmk2
fc07a38bde fix: make sure replicate pkg is the latest pkg (#1347) 2018-07-20 14:23:43 +08:00
fengmk2
17f8b6648b fix: retry from registry when no_db_file error on replicate 2018-06-08 18:42:39 +08:00
fengmk2
92acbcedcc Release 3.0.0-beta.5 2018-06-05 21:55:38 +08:00
fengmk2
d1fe6cede7 fix: add other_urls on download dist tarball (#1345) 2018-06-05 21:54:39 +08:00
fengmk2
c95cd95593 Release 3.0.0-beta.4 2018-06-05 20:23:41 +08:00
Yiyu He
8fbad397f3 fix: use rimraf instead of fs.unlink (#1338)
destory tarball when cleanup
2018-06-05 20:21:47 +08:00
fengmk2
533c27fa78 feat: support nfs.url return multi urls (#1344) 2018-06-05 19:57:13 +08:00
fengmk2
0121de31a3 fix: no need to resync again (#1336)
syncByName had retry from officialNpmRegistry when officialNpmReplicate request error
2018-05-25 10:10:17 +08:00
fengmk2
53343751f7 chore: add latest-3 tag 2018-05-21 23:44:35 +08:00
fengmk2
070fd46f36 Release 3.0.0-beta.2 2018-05-21 14:52:46 +08:00
fengmk2
e61c7fa32b feat: support pass through querystring to tgz url (#1334)
and allow bucket query params on tgz download
2018-05-21 14:50:54 +08:00
fengmk2
84a3037d90 fix: avoid query too frequently (#1329) 2018-05-02 17:45:37 +08:00
fengmk2
34d3a1eabe feat: auto sync delete packages which deleted in 24 hours (#1315) 2018-04-08 17:36:53 +08:00
fengmk2
1f60a0136c fix: replicate request error, try to request from official registry (#1316) 2018-04-08 17:25:03 +08:00
fengmk2
6f656a0736 fix: save sync last time when successes > 1000 2018-03-15 21:22:31 +08:00
fengmk2
e8f66657b4 Release 3.0.0-beta.1 2018-03-14 16:07:14 +08:00
wmzy
1b30146e94 fix: npm >= v5.5.0 login need not email (#1275) (#1304) 2018-03-14 16:04:07 +08:00
fengmk2
4210b7bdf8 feat: can config to not sync deleted versions (#1282)
avoid https://github.com/npm/registry/issues/255 again
2018-01-07 10:08:47 +08:00
fengmk2
5e264beba9 Release 3.0.0-alpha.16 2017-11-22 17:44:40 +08:00
fengmk2
56c945740f feat: let opensearch host can be config (#1258) 2017-11-22 17:42:27 +08:00
fengmk2
820ae23454 fix: control sync frequency 2017-11-08 17:51:07 +08:00
fengmk2
bfb29f82c9 fix: use _npmUser reset the maintainers 2017-10-23 11:11:25 +08:00
liang feng
95aa035a27 fix: make sure maintainers exists on sync worker
if maintainers not exists, use _npmUser instead
2017-10-23 11:05:11 +08:00
fengmk2
6c69a38a50 fix: if replicate error, retry from official registry (#1230) 2017-10-18 17:45:41 +08:00
cloudstone
43ffa995cb fix: "start" should wait for "stop" to remove the pid file(using Promise) (#1220) 2017-09-05 11:04:14 +08:00
fengmk2
6c019de514 fix: changes_stream_syncer log url should not contain sync_upstream=true 2017-08-22 09:17:13 +08:00
fengmk2
9a11df5688 Release 3.0.0-alpha.15 2017-08-22 08:58:46 +08:00
fengmk2
6b223f887a fix: don't sync upstream on every sync request (#1216)
only effect on change_stream and web sync button
2017-08-22 08:58:02 +08:00
fengmk2
9195e0966b Release 3.0.0-alpha.14 2017-08-21 19:59:13 +08:00
fengmk2
47a436afb3 fix: disable strict mode on bodyParser 2017-08-21 19:58:25 +08:00
fengmk2
ab2d269957 refactor: use koa-rt, koa-rewrite, koa-conditional-get, koa-etag
instead of koa-middlewares
2017-08-21 19:58:25 +08:00
fengmk2
ebc84b5a70 refactor: use koa-bodyparser instead of koa-middlewares.bodyParser 2017-08-21 19:58:25 +08:00
fengmk2
35a2d4e996 refactor: use yield instead of yield* 2017-08-21 19:58:25 +08:00
fengmk2
73117d826d Release 3.0.0-alpha.13 2017-08-21 14:52:42 +08:00
fengmk2
df3c9fa822 deps: upgrade markdown-it to 8 (#1214) 2017-08-21 14:51:53 +08:00
fengmk2
a0f63fddfb Release 3.0.0-alpha.12 2017-08-21 13:15:21 +08:00
fengmk2
1b53463a82 test: fix unstable test case 2017-08-21 13:14:08 +08:00
fengmk2
fc0fedcadd deps: agentkeepalive to v3.3.0
and no longger support node v4
2017-08-21 13:14:08 +08:00
fengmk2
ee79a80fc0 Release 3.0.0-alpha.11 2017-08-17 13:45:51 +08:00
fengmk2
ee9bf331c4 fix: list since limit to 2 days 2017-08-17 00:44:45 -05:00
fengmk2
5b63dbe514 feat: list all private packages names 2017-08-17 00:44:45 -05:00
fengmk2
32434b22dc feat: trigger package:sync event with changedVersions 2017-08-17 00:44:45 -05:00
fengmk2
bb77186bbc test: should show deprecated info on abbreviatedMeta request (#1193) 2017-06-19 17:57:58 +08:00
fengmk2
d52259f2ee Release 3.0.0-alpha.8 2017-06-15 13:27:30 +08:00
fengmk2
da70d1ecf8 fix: should remove unpublished version on ModuleAbbreviated too (#1192) 2017-06-15 13:25:55 +08:00
lvs(zikuan.ly)
e73b22d6d3 docs: Dockerized cnpmjs.org configuration with installation guide (#1191)
* add dockerizing file
* using mysql for dockerized cnpmjsorg
* add configure file for the dockrized instance
* update dockerized configuration for work properly on test env
* add dockerized cnpmjs.org install guide
* add named Docker volume
* update dockerized configuration, enable abbreviated meta data by default
2017-06-13 18:08:03 +08:00
fengmk2
10c8a1ecf0 Release 3.0.0-alpha.7 2017-06-01 00:31:37 +08:00
fengmk2
9b48106943 fix: add missing publish_time property on package list api (#1185)
https://github.com/cnpm/npminstall/pull/221#issuecomment-294904740
2017-06-01 00:05:33 +08:00
fengmk2
49bb2a81f5 Release 3.0.0-alpha.6 2017-05-18 15:43:32 +08:00
fengmk2
65c0ad8eb8 feat: add globalHook on config (#1177)
* feat: add globalHook on config

Remote hooks need to wait for
https://github.com/cnpm/cnpmjs.org/issues/624
2017-05-18 15:41:47 +08:00
Jackson Tian
345d6d5dfc fix: TypeError caused by invalid engines property (#1151) 2017-04-17 00:35:05 +08:00
fengmk2
9656111ef4 test: add new test for application/vnd.npm.install-v1+json request 2017-04-17 00:28:03 +08:00
fengmk2
c9395b983c Release 3.0.0-alpha.5 2017-04-14 10:00:04 +08:00
fengmk2
ccd05af9d0 fix: should auto sync missing deprecated property (#1167) 2017-04-14 09:59:11 +08:00
fengmk2
0e5586f4f4 Release 3.0.0-alpha.4 2017-04-12 13:51:22 +08:00
fengmk2
00fc931291 fix: add missing deprecated on abbreviated meta (#1165) 2017-04-12 13:49:00 +08:00
fengmk2
81ce21bfbf Release 3.0.0-alpha.3 2017-04-06 17:32:23 +08:00
fengmk2
707bf663e5 feat: support abbreviated meta for cnpmjs.org private package (#1160) 2017-04-06 17:31:21 +08:00
fengmk2
abfd997d0c fix: try to sync from officialNpmRegistry when replicate is 404 2017-03-27 22:35:28 +08:00
fengmk2
358b41bc0f Release 3.0.0-alpha.2 2017-03-27 17:41:39 +08:00
fengmk2
d984cd9177 fix: only get from package_readme table 2017-03-27 16:26:01 +08:00
fengmk2
90b5368a05 Release 3.0.0-alpha.1 2017-03-27 15:50:55 +08:00
fengmk2
842bb7fa11 chore: start 3.x 2017-03-27 15:49:34 +08:00
fengmk2
f55c6ba1c6 fix: ignore sync npm registry status 502 2017-03-27 15:49:34 +08:00
fengmk2
cd21e800fa feat: remove readme from package 2017-03-27 15:49:34 +08:00
fengmk2
8cf3344b68 feat: [BREAKING_CHANGE] support abbreviated meta
- https://github.com/cnpm/cnpmjs.org/issues/1149
- https://github.com/cnpm/cnpmjs.org/issues/1148
2017-03-27 15:49:34 +08:00
fengmk2
373b535bc5 Release 2.19.4 2017-03-26 23:23:41 +08:00
fengmk2
2daa34faab feat: need to sync sourceNpmRegistry also (#1153)
make sure package data be update event replicate down.

https://github.com/npm/registry/issues/129
2017-03-24 00:18:34 +08:00
fengmk2
10170537dd docs: change user.json to utf8mb4 2017-03-09 10:29:57 +08:00
fengmk2
598dc08ef6 Release 2.19.3 2017-02-22 00:06:28 +08:00
fengmk2
f0238f6063 fix: should get package from orginal registry when package is unpublished (#1130) 2017-02-22 00:05:29 +08:00
fengmk2
0d86d05d45 Release 2.19.2 2017-01-05 19:41:23 +08:00
fengmk2
1598c2a3d9 fix: should auto sync un-deprecate message (#1105) 2017-01-05 19:40:36 +08:00
fengmk2
997e9c5cb5 Release 2.19.1 2016-12-29 00:54:44 +08:00
Yuwei Ba
9cfe0502dd fix: try to use the best repository url (#1102) 2016-12-29 00:53:54 +08:00
fengmk2
ff2536fcc5 Release 2.19.0 2016-12-21 00:26:09 +08:00
21paradox
df5564b1ea feat: keyword search with limit to support keywords > 100 (#1097)
max limit to 10000
2016-12-21 00:24:31 +08:00
fengmk2
093031e720 Release 2.18.0 2016-12-05 21:39:19 +08:00
fengmk2
65b595b899 fix: support downloads total on scope package (#1088)
closes https://github.com/cnpm/cnpmjs.org/issues/1078
2016-12-05 21:38:38 +08:00
fengmk2
bf5f02908f fix: try to sync from official replicate (#1076) 2016-11-20 01:27:44 +08:00
alsotang
6c3140d422 feat: add change password script (#1070) 2016-11-17 21:03:31 +08:00
fengmk2
dd27edc36b test: skip always fail tests 2016-11-16 03:03:08 +08:00
fengmk2
2065c975e0 test: add node v7 2016-11-16 03:03:08 +08:00
fengmk2
0020a33e65 feat: show more sync info 2016-11-16 03:03:08 +08:00
fengmk2
dd423c08d8 Release 2.17.2 2016-11-13 23:26:39 +08:00
fengmk2
f9f049c177 fix: ignore long package name on unpublished sync (#1067)
closes https://github.com/cnpm/cnpmjs.org/issues/1066
2016-11-13 23:23:59 +08:00
fengmk2
034bdbb344 Release 2.17.1 2016-11-08 17:26:45 +08:00
Yiyu He
d7d4cb7fa7 fix: add publish_time for private packages (#1061) 2016-11-08 17:24:07 +08:00
fengmk2
b83483e3c0 Release 2.17.0 2016-11-03 00:02:38 +08:00
fengmk2
9b909b3233 feat: make snyk.io url configable (#1058)
closes https://github.com/cnpm/mirrors/issues/116
2016-11-03 00:01:41 +08:00
fengmk2
fac9df1de5 Release 2.16.2 2016-09-27 18:16:42 +08:00
fengmk2
3262adadd4 fix: try to use config.registryHost first on setDownloadURL (#1044) 2016-09-27 18:16:07 +08:00
fengmk2
74e9bdde72 Release 2.16.1 2016-08-22 21:51:23 +08:00
Yiyu He
f567ef5567 refactor: refine publishable's code (#1022) 2016-08-22 21:48:47 +08:00
fengmk2
c89d7023c4 Release 2.16.0 2016-08-22 19:42:27 +08:00
Yiyu He
72aa64258f feat: admin can do everything (#1021) 2016-08-22 19:04:59 +08:00
dead_horse
b5b63c11f2 Release 2.15.0 2016-08-22 14:34:38 +08:00
fengmk2
4da684f3b6 feat: return dist-tag on package registry (#1020) 2016-08-22 14:09:05 +08:00
Greenkeeper
fb29419c27 chore(package): update supertest to version 2.0.0 (#1004)
https://greenkeeper.io/
2016-08-04 08:43:29 +08:00
fengmk2
8789dadaef Release 2.14.0 2016-08-04 08:42:37 +08:00
wenbing
1c936d4b73 feat: password may contains ":" (#999) 2016-08-04 08:40:12 +08:00
fengmk2
7c2f947518 fix: limit sync fails email notice (#1006)
and skip JSONResponseFormatError and 504 server error
2016-08-02 21:42:31 +08:00
fengmk2
bfbfef87d2 Release 2.13.0 2016-07-26 18:33:32 +08:00
fengmk2
ea3beceef7 feat: enable maxrequests middleware (#1003) 2016-07-26 18:32:33 +08:00
fengmk2
b0f0bc0331 Release 2.12.2 2016-07-11 12:48:36 +08:00
Yiyu He
932547af2a fix: getModuleByRange don't list all packages (#990) 2016-07-11 12:46:52 +08:00
fengmk2
79ac9fb823 fix: should show new version package count (#984) 2016-07-04 00:06:42 +08:00
fengmk2
a437eb01eb Release 2.12.1 2016-07-01 17:59:38 +08:00
fengmk2
6d921a9c46 fix: make sure chagnes stream destroy (#982)
closes #981
2016-07-01 17:58:24 +08:00
Greenkeeper
abcb81d28c chore(package): update semver to version 5.2.0 (#978)
https://greenkeeper.io/
2016-07-01 15:27:10 +08:00
fengmk2
ea54f32805 deps: use ^ instead of ~ (#976) 2016-06-27 01:01:23 +08:00
Greenkeeper
3dbf29afac chore(package): update mini-logger to version 1.1.1 (#973)
https://greenkeeper.io/
2016-06-26 13:32:53 +08:00
dead_horse
2c520d97a9 Release 2.12.0 2016-06-26 13:30:41 +08:00
fengmk2
38148b24cb fix: logger seperator should be one EOL (#972) 2016-06-26 13:28:42 +08:00
fengmk2
97895bdfa5 feat: add security check badge for public package (#971) 2016-06-26 13:18:33 +08:00
dead_horse
0c1c12680c Release 2.11.0 2016-06-25 21:06:08 +08:00
fengmk2
6485e4b019 feat: add changes stream syncer (#970)
* feat: add changes stream syncer

fix scope package missing problem

close #908

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

closes #938

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

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

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

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

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

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

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

return user packages list

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

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

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

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

closes cnpm/cnpm#64
closes cnpm/cnpm#61
2015-07-27 01:20:30 +08:00
fengmk2
7f89b3be92 test: fix fails tests 2015-07-26 20:10:48 +08:00
fengmk2
1c6eb50e05 fix: ignore 503 server error 2015-07-26 20:02:48 +08:00
fengmk2
aa8d4ac850 fix: ignore sync 503 server error 2015-07-26 19:54:07 +08:00
fengmk2
e0eeefa43e Release 2.1.0 2015-07-08 23:21:19 +08:00
Yiyu He
662b91ea9a Merge pull request #630 from cnpm/search-support-jsonp
feat(web): search support jsonp
2015-07-08 22:19:55 +08:00
fengmk2
cc70f9e6b1 feat(web): search support jsonp
http://localhost:7002/browse/keyword/mk2?type=json&callback=foo
2015-07-08 21:06:14 +08:00
dead_horse
7312d761ca fix function name 2015-06-15 23:10:39 +08:00
fengmk2
d5c0e07c1c Release 2.0.0 2015-05-11 22:48:35 +08:00
fengmk2
e108b4804d Merge pull request #608 from cnpm/http-proxy
feat(urllib): support http_proxy
2015-05-11 15:25:33 +08:00
Yiyu He
92c218d755 Merge pull request #610 from cnpm/download-as-stream
fix: real download as stream
2015-04-20 00:47:02 +08:00
fengmk2
df2a974b2c fix: real download as stream 2015-04-20 00:40:06 +08:00
fengmk2
7fc1a8411c add custom ad banner config 2015-03-28 09:53:44 +08:00
fengmk2
0759328140 add sponsors: ucloud.cn 2015-03-23 09:31:02 +08:00
Yiyu He
3fc2ea7f25 Merge pull request #609 from limianwang/master
fix small typo
2015-03-09 14:14:19 +08:00
Limian Wang
2f2794785f fix small typo 2015-03-08 23:03:52 -07:00
fengmk2
ea1f253624 feat(urllib): support http_proxy
Fixes #607
2015-03-09 11:25:05 +08:00
fengmk2
b25eaaf73e force using https links 2015-02-16 11:19:18 +08:00
fengmk2
0b0164cbf4 Release 2.0.0-rc.15 2015-02-15 11:07:21 +08:00
Yiyu He
8a727c351d Merge pull request #602 from cnpm/pg
feat(database): support PostgreSQL
2015-02-14 23:52:26 +08:00
fengmk2
2c6a08908d fix(markdown): filter xss after markdown render 2015-02-14 22:02:31 +08:00
fengmk2
9a7e94f7a4 feat(database): support PostgreSQL
And the big problem of pg is bigint will be convert to string

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

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

Fixes #600
2015-02-13 20:39:40 +08:00
fengmk2
4e3a50f3bb fix mysql select args = [] bug 2015-02-13 17:51:50 +08:00
fengmk2
e5a9a22f86 fix #597 sequelize raw query. 2015-02-13 17:40:40 +08:00
fengmk2
eb66d8958f fix(markdown): hotfix markdown-it cpu problem
Fixes #598
2015-02-13 17:14:23 +08:00
Yiyu He
8a0dcb5063 Merge pull request #596 from cnpm/co4
feat: upgrade to co4
2015-02-10 09:43:16 +08:00
fengmk2
cba9d5dbbb feat: upgrade to co4 2015-02-10 09:00:55 +08:00
Yiyu He
233b736401 Merge pull request #595 from cnpm/kcors
use kcors fixes #594
2015-02-09 14:05:28 +08:00
fengmk2
58d72bde22 use kcors fixes #594 2015-02-09 14:01:03 +08:00
fengmk2
38683a82ef Release 2.0.0-rc.13 2015-02-04 01:28:11 +08:00
Yiyu He
71cc54fb3b Merge pull request #592 from cnpm/easy-deploy
Deploy a private npm registry in 5 minutes
2015-02-04 01:24:11 +08:00
fengmk2
b0eb611dc4 use gnode to make sure harmony enable 2015-02-04 01:08:51 +08:00
fengmk2
7e810dba48 docs: Deploy a private npm registry in 5 minutes 2015-02-03 23:15:06 +08:00
fengmk2
f4dc41ede5 bump deps 2015-02-03 19:48:31 +08:00
fengmk2
5574301909 refactor(config): move application data to ~/.cnpmjs.org/ 2015-02-03 19:43:53 +08:00
Yiyu He
261c97f1e8 Merge pull request #590 from cnpm/sync-none-proxy-to-npm
feat(sync): enable none syncModel proxy all public packages
2015-02-03 18:21:46 +08:00
fengmk2
af8ae63eab fix(sync): make get popular pakcage faster
Fixes #591
2015-02-03 18:09:51 +08:00
fengmk2
89e7ae3ba2 feat(sync): web page also redirect to npm www 2015-02-03 17:28:08 +08:00
fengmk2
46d65a1878 refactor(config): make syncModel to none by default 2015-02-03 15:50:34 +08:00
fengmk2
77074e3ae3 test: fix admin can not publish non-scoped package test cases 2015-02-03 15:15:17 +08:00
dead_horse
34fa887aa8 docs: add chinese mirror link 2015-02-03 01:16:24 +08:00
fengmk2
6dc779a9b4 fix: admin can not publish non scoped package on "none" sync model 2015-02-03 00:51:37 +08:00
fengmk2
24d6831a68 feat(sync): enable none syncModel proxy all public packages
Fixes #589 #581
2015-02-03 00:33:50 +08:00
fengmk2
d83c52b32f Merge pull request #588 from cnpm/stat-user-syncing
Star user syncing bugfix
2015-02-01 21:52:34 +08:00
fengmk2
25334e5d95 fix: ignore username start with " or '
Fixes #583
2015-02-01 21:49:25 +08:00
fengmk2
e27af73ccc remove unused files 2015-02-01 21:33:49 +08:00
fengmk2
f85da0a384 fix(bin): fix stop not work on iojs 2015-02-01 16:49:48 +08:00
fengmk2
1b6159132d Release 2.0.0-rc.12 2015-02-01 16:44:09 +08:00
fengmk2
2130041f7f Merge pull request #587 from cnpm/syncer-ua
feat(syncer): add hostname ua
2015-02-01 16:43:03 +08:00
fengmk2
b5646e12a6 feat(syncer): add hostname ua
Fixes #584
2015-02-01 15:51:54 +08:00
fengmk2
d70c284b83 Merge pull request #586 from cnpm/remove-contributor
fix: remove pkg.contributors logic
2015-02-01 15:26:40 +08:00
dead_horse
ddbb0b4fed fix: remove pkg.contributors logic 2015-02-01 15:18:22 +08:00
fengmk2
35c1a956f7 Release 2.0.0-rc.11 2015-02-01 00:23:09 +08:00
fengmk2
416e2650cc fix xss tests 2015-02-01 00:22:38 +08:00
Yiyu He
964cfd5242 Merge pull request #582 from cnpm/revert-marky-markdown
fix(markdown): revert marky-markdown
2015-02-01 00:21:02 +08:00
fengmk2
dd5ac02876 fix(markdown): revert marky-markdown
We dont want to use cpp module
2015-02-01 00:19:05 +08:00
fengmk2
54a11ebb17 Release 2.0.0-rc.10 2015-01-31 22:55:01 +08:00
Yiyu He
9131dcb105 Merge pull request #579 from cnpm/cors
feat(middleware): CORS headers for GET and HEAD requests
2015-01-31 22:50:07 +08:00
fengmk2
6e276c58c2 feat(middleware): CORS headers for GET and HEAD requests
Fixes #570
2015-01-31 21:35:17 +08:00
Yiyu He
7b1929b717 Merge pull request #580 from cnpm/marky-markdown
feat(markdown): use npm same markdown parser
2015-01-31 17:50:24 +08:00
fengmk2
ec4a74f439 fix(readme): fix index page markdown 2015-01-31 17:45:28 +08:00
fengmk2
027d2bb46b feat(markdown): use npm same markdown parser
Fixes #574
2015-01-31 14:27:15 +08:00
Yiyu He
2b6bd06bec Merge pull request #578 from cnpm/download-redirect-to-nfs
feat(download): support download redirect to nfs
2015-01-31 00:44:59 +08:00
fengmk2
ff523d7374 feat(download): support download redirect to nfs 2015-01-31 00:04:02 +08:00
fengmk2
931afd7f4b Merge pull request #576 from cnpm/npm-request-gzip-enable
feat(syncer): request npm registry with gzip
2015-01-30 20:55:59 +08:00
fengmk2
f2724d959f feat(syncer): request npm registry with gzip
Fixes #575
2015-01-30 20:55:40 +08:00
Yiyu He
ca884d92ca Merge pull request #573 from cnpm/remove-dist
change(sync): remove dist syncer
2015-01-29 12:02:29 +08:00
fengmk2
44c8e05563 change(sync): remove dist syncer
move to https://github.com/cnpm/mirrors

Fixes #523 #504 #307
2015-01-29 10:18:10 +08:00
Yiyu He
8c0f9057ac Merge pull request #572 from cnpm/dist-tags-endpoints
Dist tags endpoints
2015-01-28 22:58:45 +08:00
fengmk2
fde319a788 feat(registry): add dist tag api
Fixes #568
2015-01-28 17:11:47 +08:00
fengmk2
c6704782a7 refactor(common): remove redis store 2015-01-28 11:46:43 +08:00
dead_horse
491b91ac0e Release 2.0.0-rc.9 2015-01-22 12:33:33 +08:00
dead_horse
f7819186e7 hotfix reame render error, pin xss 2015-01-22 12:32:54 +08:00
Yiyu He
4652eca74e Merge pull request #564 from cnpm/update-registry-api
fix registry user auth api
2015-01-16 11:41:04 +08:00
fengmk2
ebb7cbcf68 fix registry user auth api 2015-01-16 11:38:08 +08:00
fengmk2
20b98b1227 update authors 2015-01-10 16:33:33 +08:00
fengmk2
7e288a442c Release 2.0.0-rc.8 2015-01-10 13:06:31 +08:00
fengmk2
17d1527551 fix(markdown): readme.md allow scripts 2015-01-10 13:04:08 +08:00
Yiyu He
4de50e2bf8 Merge pull request #561 from afc163/patch-1
fix(style) flexbox compatibility for both chrome and firefox
2015-01-09 13:39:08 +08:00
偏右
80bf9d2d73 fix(style) flexbox compatibility for both chrome and firefox 2015-01-09 13:36:39 +08:00
fengmk2
7d8f6ec92f Merge pull request #559 from cnpm/default-sync-exist
feat(sync): default sync exist packages
2015-01-07 11:37:15 +08:00
dead_horse
1bbf4d7ebe feat(sync): default sync exist packages 2015-01-07 11:04:50 +08:00
fengmk2
88f8cb8fa9 Release 2.0.0-rc.7 2015-01-07 01:57:32 +08:00
fengmk2
7c59fa5e37 install sync dont check enablePrivate
Fixes #549
2015-01-07 01:56:08 +08:00
fengmk2
ccb24025b5 Merge pull request #555 from cnpm/filter-xss-before-markdown-render
fix(markdown): filter xss readme before markdown render
2015-01-05 18:37:13 +08:00
fengmk2
6f24c10cad fix(markdown): filter xss readme before markdown render
Fixes #554
2015-01-05 18:27:56 +08:00
fengmk2
2579b62e3a Release 2.0.0-rc.6 2015-01-05 10:44:37 +08:00
fengmk2
38ed0a91b2 fix(markdown): use markdown-it
Fixes #552
2015-01-04 23:27:07 +08:00
fengmk2
57a025d96e add userService options on config 2015-01-04 15:50:59 +08:00
fengmk2
eef7467321 add upload to nfs sync info log 2015-01-04 10:59:21 +08:00
fengmk2
4c89b8a4c1 Release 2.0.0-rc.5 2015-01-03 02:46:24 +08:00
fengmk2
353177deee Merge pull request #553 from cnpm/hotfix-markdown
fix(markdown): use marked instead of remarkable
2015-01-03 02:30:08 +08:00
fengmk2
603fdeea06 fix(markdown): use marked instead of remarkable
Fixes #552
2015-01-03 02:25:39 +08:00
fengmk2
1850d33b83 Merge pull request #550 from cnpm/sync-user-profile
Sync user profile
2015-01-03 01:18:02 +08:00
fengmk2
8827ca26a7 fix(package): pkg.readme is not a string, dont remarkable it
Fixes #551
2015-01-03 00:52:56 +08:00
fengmk2
9e34566a19 test(web): change position 2015-01-02 09:25:28 +08:00
fengmk2
8e4fd68cca feat(sync): sync user profile
Fixes #535
2015-01-02 09:22:29 +08:00
fengmk2
a8fbeb7be5 Release 2.0.0-rc.4 2014-12-25 16:06:34 +08:00
fengmk2
cd0249dfb9 fix sync_download_total.js 2014-12-25 14:52:46 +08:00
fengmk2
e34d1948fb refactor(download): try to use nsf.url() first 2014-12-25 11:11:38 +08:00
Yiyu He
4cd46b1857 Merge pull request #545 from cnpm/refactor-downloads
Refactor downloads
2014-12-23 10:56:51 +08:00
fengmk2
c8a7343d45 use __all__ for full downloads 2014-12-23 09:35:13 +08:00
fengmk2
0689cc93ce refactor(download_total): optimize download total
Use downloads table instead of download_total

Fixes #544
2014-12-23 00:45:09 +08:00
fengmk2
6c98c1f07f Merge pull request #543 from cnpm/slow-sql
fix(download_total): change column date to DateTime
2014-12-22 00:19:40 +08:00
fengmk2
94021b0553 fix sqlite raw sql return datetime is string format
Also add sql change wiki: https://github.com/cnpm/cnpmjs.org/wiki/Migrating-from-1.x-to-2.x#sql-changes
2014-12-22 00:14:46 +08:00
fengmk2
9ed3b3ad7a fix(download_total): change column date to DateTime
Fixes #540
2014-12-21 22:51:45 +08:00
fengmk2
d651c2ee6f Merge pull request #542 from cnpm/slow-sql
fix(services/download_total): fix download_total slow sql
2014-12-21 22:14:15 +08:00
fengmk2
3653daf261 fix(services/download_total): fix download_total slow sql on date >= $start and date <= $end
Use `date in ($ranges)` instead.

Closes #540
2014-12-21 22:06:45 +08:00
Yiyu He
01a722190f Merge pull request #539 from cnpm/remove-marked
fix(markdown): replace marked use remarkable
2014-12-16 23:33:29 +08:00
fengmk2
4aec25d4ab fix(markdown): replace marked use remarkable 2014-12-16 18:37:31 +08:00
fengmk2
2049978a68 Release 2.0.0-rc.3 2014-12-14 00:58:48 +08:00
fengmk2
93b98bcb7e Merge pull request #534 from cnpm/hotfix-empty-sql
fix(services): need to detect instance isDirty or not before save()
2014-12-14 00:57:38 +08:00
fengmk2
4838efc141 fix(services): need to detect instance isDirty or not before save()
This error start on sequelize@v2.0.0-rc3

Closes #533
2014-12-14 00:52:49 +08:00
fengmk2
1e48c47010 Release 2.0.0-rc.2 2014-12-11 01:38:39 +08:00
fengmk2
81d742d210 Merge pull request #530 from cnpm/issue529-download
add download API
2014-12-11 01:31:11 +08:00
dead_horse
665d72ed32 add download API, closes #529 2014-12-11 01:09:56 +08:00
Yiyu He
c70888d7ac Merge pull request #528 from rockdai/master
fix missing home page title
2014-12-10 00:56:50 +08:00
巴思
5acda5ee57 fix missing home page title 2014-12-10 00:50:17 +08:00
Yiyu He
84587bfa86 Merge pull request #527 from LoicMahieu/patch-1
Fix typo in view/web/package.html
2014-12-09 22:42:42 +08:00
Loïc Mahieu
263910c13c Fix typo in view/web/package.html
Change `Reopsitory` to `Repository`
2014-12-09 15:40:54 +01:00
fengmk2
e6667cceb8 Release 2.0.0-rc.1 2014-12-09 16:03:54 +08:00
fengmk2
b5bfef5160 fix xss on title 2014-12-09 16:03:06 +08:00
Yiyu He
0cbf8e377f Merge pull request #522 from cnpm/badge-url-tag
feat(badge): support badge image url with tag
2014-12-06 17:16:27 +08:00
fengmk2
0e1fdb1c05 feat(badge): support badge image url with tag
Closes #520
2014-12-06 16:31:18 +08:00
241 changed files with 13476 additions and 6928 deletions

2
.dockerignore Normal file
View File

@@ -0,0 +1,2 @@
node_modules
npm-debug.log

1
.gitignore vendored
View File

@@ -29,3 +29,4 @@ coverage/
config/web_readme.md
.tmp/
*.sqlite

View File

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

View File

@@ -22,3 +22,4 @@ config/web_readme.md
config/config.js
*.sqlite
.node-dev.json
nohup.out

View File

@@ -1,6 +1,10 @@
language: node_js
node_js:
- '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"
- '10'
- '12'
services:
- mysql
- postgresql
script: 'make test-travis-all'
after_script:
- 'npm i codecov && codecov'

View File

@@ -1,8 +1,12 @@
# Ordered by date of first contribution.
# 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)
dead-horse <dead_horse@qq.com> (https://github.com/dead-horse)
alsotang <alsotang@gmail.com> (https://github.com/alsotang)
4simple <wondger@qq.com> (https://github.com/4simple)
afc163 <afc163@gmail.com> (https://github.com/afc163)
Yuwei Ba <i@xiaoba.me> (https://github.com/ibigbug)
dickeylth <dickeylth@gmail.com> (https://github.com/dickeylth)
wenbing <wenbing@users.noreply.github.com> (https://github.com/wenbing)
21paradox <1036339815@qq.com> (https://github.com/21paradox)

28
Dockerfile Normal file
View File

@@ -0,0 +1,28 @@
FROM node:12
MAINTAINER Bono Lv <lvscar {aT} gmail.com>
# Working enviroment
ENV \
CNPM_DIR="/var/app/cnpmjs.org" \
CNPM_DATA_DIR="/var/data/cnpm_data"
RUN mkdir -p ${CNPM_DIR}
WORKDIR ${CNPM_DIR}
COPY package.json ${CNPM_DIR}
RUN npm set registry https://registry.npm.taobao.org
RUN npm install --production
COPY . ${CNPM_DIR}
COPY docs/dockerize/config.js ${CNPM_DIR}/config/
EXPOSE 7001/tcp 7002/tcp
VOLUME ["/var/data/cnpm_data"]
# Entrypoint
CMD ["node", "dispatch.js"]

View File

@@ -1,8 +1,654 @@
3.0.0-rc.44 / 2021-06-11
==================
**features**
* [[`a1e8a82`](https://github.com/cnpm/cnpmjs.org/commit/a1e8a8289276275b995d15b3c254fbdbace6dbae)] - feat: impl npm owner hooks (#1645) (killa <<killa123@126.com>>)
3.0.0-rc.43 / 2021-05-36
==================
**features**
* [[`a21aed0`](https://github.com/cnpm/cnpmjs.org/commit/a21aed08c5fe1ea09f4fda157ac3c12bd609781d)] - feat: impl sync to/from backup files (#1612) (killa <<killa123@126.com>>)
**fixes**
* [[`2245dc2`](https://github.com/cnpm/cnpmjs.org/commit/2245dc2967ec070b8bcc618ebfad0cd4cd297fb8)] - feat: impl accelerate request (#1637) (killa <<killa123@126.com>>)
3.0.0-rc.42 / 2021-04-30
==================
**features**
* [[`a21aed0`](https://github.com/cnpm/cnpmjs.org/commit/a21aed08c5fe1ea09f4fda157ac3c12bd609781d)] - feat: impl sync to/from backup files (#1612) (killa <<killa123@126.com>>)
3.0.0-rc.39 / 2021-01-14
==================
**features**
* [[`33dd355`](http://github.com/cnpm/cnpmjs.org/commit/33dd3554f5daf13de33f04128be6853ce120636f)] - feat: impl dist-tag hooks (#1612) (killa <<killa123@126.com>>)
3.0.0-rc.38 / 2021-01-12
==================
**features**
* [[`c6040b`](http://github.com/cnpm/cnpmjs.org/commit/1c6040bc95610e23b4756baa09e8119cda2fe01e)] - performance: optimise query pkg latestModified (#1611) (killa <<killa123@126.com>>)
3.0.0-rc.37 / 2020-10-21
==================
**features**
* [[`39c3223`](http://github.com/cnpm/cnpmjs.org/commit/39c322332ffafc512bf56c1679d2904fece2ae07)] - feat: new registry api (#1597) (killa <<killa123@126.com>>)
* [[`45f2f8b`](http://github.com/cnpm/cnpmjs.org/commit/45f2f8b31f095eeadf0f47e234d6eb225e6b197f)] - feat: impl registry token api (#1590) (killa <<killa123@126.com>>)
* [[`e97835f`](http://github.com/cnpm/cnpmjs.org/commit/e97835f7020e945e59fa7a84b14ab58c580add1e)] - feat: support custom web middlewares (#1563) (fengmk2 <<fengmk2@gmail.com>>)
* [[`3cb3fe0`](http://github.com/cnpm/cnpmjs.org/commit/3cb3fe02f01dd669ad4bd3aebca51c44eb9e5938)] - feat: list all package versions by date (#1557) (fengmk2 <<fengmk2@gmail.com>>)
* [[`a8ff647`](http://github.com/cnpm/cnpmjs.org/commit/a8ff647aa0f73076f4625e395e5da8ced9f61680)] - feat: retry sync fail on cnpm registry (#1547) (fengmk2 <<fengmk2@gmail.com>>)
* [[`2c511f2`](http://github.com/cnpm/cnpmjs.org/commit/2c511f2209329e95b0cbe7603fa98a7f93c66474)] - feat: add unpublishRemoveTarball mode (#1536) (Khaidi Chu <<i@2333.moe>>)
* [[`19563f5`](http://github.com/cnpm/cnpmjs.org/commit/19563f58517ffaebed8006630bd467f15b71d9ff)] - feat: allow to disable npm audits proxy (#1430) (fengmk2 <<fengmk2@gmail.com>>)
* [[`8e2367e`](http://github.com/cnpm/cnpmjs.org/commit/8e2367ee1676bd36a4112cf0f6dce2c4f422806e)] - feat: dont check db data on tgz download request (#1477) (fengmk2 <<fengmk2@gmail.com>>)
* [[`be05886`](http://github.com/cnpm/cnpmjs.org/commit/be05886452803d46f614bdde497ccdec8e9ed734)] - feat: add vary header on cdn (fengmk2 <<fengmk2@gmail.com>>)
* [[`ea46399`](http://github.com/cnpm/cnpmjs.org/commit/ea46399265615c70ee33d9cab9ba5d5ce312fb67)] - feat: allow disable search page (fengmk2 <<fengmk2@gmail.com>>)
* [[`581925d`](http://github.com/cnpm/cnpmjs.org/commit/581925db9733d295be02e75f0090db05fd6bae75)] - feat: support cache-control header on registry request (#1468) (fengmk2 <<fengmk2@gmail.com>>)
* [[`7f0c141`](http://github.com/cnpm/cnpmjs.org/commit/7f0c141ac2f7679b5322aadd537c5ff1bef0b032)] - feat: allow config request protocol (fengmk2 <<fengmk2@gmail.com>>)
* [[`807187e`](http://github.com/cnpm/cnpmjs.org/commit/807187ebeb0b266828a59724234e1a99c3238eb3)] - feat: add redis cache to import list all versions api perf (#1441) (fengmk2 <<fengmk2@gmail.com>>)
* [[`99c4c3f`](http://github.com/cnpm/cnpmjs.org/commit/99c4c3fe35a9fde805751ef3d44413413f053f45)] - feat: support customized middlewares (#1436) (Khaidi Chu <<i@2333.moe>>)
* [[`4b57c11`](http://github.com/cnpm/cnpmjs.org/commit/4b57c118a0b044f41b1c98eaf92449221c984c15)] - feat: can override tgz download options (fengmk2 <<fengmk2@gmail.com>>)
* [[`b395c66`](http://github.com/cnpm/cnpmjs.org/commit/b395c666be3ae6b237803239fae8678647f3b70b)] - feat: proxy npm audit request (#1419) (alsotang <<alsotang@gmail.com>>)
* [[`a4a25f9`](http://github.com/cnpm/cnpmjs.org/commit/a4a25f9e381aa20e1ef1e709f320aae41f3ae466)] - feat: use faster etag instead of koa-etag (#1409) (fengmk2 <<fengmk2@gmail.com>>)
* [[`90580a7`](http://github.com/cnpm/cnpmjs.org/commit/90580a72e56c69f8f03bbdb64d79b4b1b139fbbf)] - feat: configurable view directory (#1400) (Khaidi Chu <<i@2333.moe>>)
* [[`ad2d341`](http://github.com/cnpm/cnpmjs.org/commit/ad2d341d2c9317b062a25363f4805aedfef3913b)] - feat: sync downloads total <= 10000 unpublish package (fengmk2 <<fengmk2@gmail.com>>)
* [[`25a9030`](http://github.com/cnpm/cnpmjs.org/commit/25a90300473e6ac437e393de139cebde1e354e8c)] - feat: allow to close mysql trace (fengmk2 <<fengmk2@gmail.com>>)
* [[`017af69`](http://github.com/cnpm/cnpmjs.org/commit/017af69cce23c870694d124f4a865864e5c061cd)] - feat: add badgeService define on config (#1387) (fengmk2 <<fengmk2@gmail.com>>)
* [[`842c031`](http://github.com/cnpm/cnpmjs.org/commit/842c0316ede2b19b76d9c1ca790902de467c82e9)] - feat: show versions list on package page (#1386) (fengmk2 <<fengmk2@gmail.com>>)
* [[`bd87907`](http://github.com/cnpm/cnpmjs.org/commit/bd87907b69d3e65aa544930b5c7f04e75bdbc773)] - feat: auto retry if download tgz error (#1363) (fengmk2 <<fengmk2@gmail.com>>)
* [[`533c27f`](http://github.com/cnpm/cnpmjs.org/commit/533c27fa78323ee50fcd549115034915ea3017ef)] - feat: support nfs.url return multi urls (#1344) (fengmk2 <<fengmk2@gmail.com>>)
* [[`e61c7fa`](http://github.com/cnpm/cnpmjs.org/commit/e61c7fa32bdc54ef4474a071da686c70e512b009)] - feat: support pass through querystring to tgz url (#1334) (fengmk2 <<fengmk2@gmail.com>>)
* [[`34d3a1e`](http://github.com/cnpm/cnpmjs.org/commit/34d3a1eabe927dc5c8c87436e2b644c70a7abc2a)] - feat: auto sync delete packages which deleted in 24 hours (#1315) (fengmk2 <<fengmk2@gmail.com>>)
* [[`4210b7b`](http://github.com/cnpm/cnpmjs.org/commit/4210b7bdf8bfe8dfa2578802fd1d14e7411d4ea6)] - feat: can config to not sync deleted versions (#1282) (fengmk2 <<fengmk2@gmail.com>>)
* [[`56c9457`](http://github.com/cnpm/cnpmjs.org/commit/56c945740f545abe9ba55759f6b1502a3abc453d)] - feat: let opensearch host can be config (#1258) (fengmk2 <<fengmk2@gmail.com>>)
**fixes**
* [[`b7089d3`](http://github.com/cnpm/cnpmjs.org/commit/b7089d33d400f9fd4fc398479d4dac5aab26b633)] - fix: set maintainer to current user if maintainer is undefined (#1592) (killa <<killa123@126.com>>)
* [[`2b74e00`](http://github.com/cnpm/cnpmjs.org/commit/2b74e00cb9ae20e9cf2f06c54ef8dbe6a36b4066)] - fix: release 3.0.0-rc.35 fix npm include functions dir (fengmk2 <<fengmk2@gmail.com>>)
* [[`61549b4`](http://github.com/cnpm/cnpmjs.org/commit/61549b47a2f49c163bef6994f1e0f5f761317975)] - fix: avoid "ENAMETOOLONG: name too long" error (#1583) (fengmk2 <<fengmk2@gmail.com>>)
* [[`e7bafb2`](http://github.com/cnpm/cnpmjs.org/commit/e7bafb2ee9d80ce3ef4087a6b69bc17517f85ec5)] - fix: audit proxy test cases (#1537) (Khaidi Chu <<i@2333.moe>>)
* [[`92b7216`](http://github.com/cnpm/cnpmjs.org/commit/92b72169a89cec333177d1ba65205a31e60ebbb2)] - fix: maintainer permission greater than scope (#1494) (Khaidi Chu <<i@2333.moe>>)
* [[`f084eba`](http://github.com/cnpm/cnpmjs.org/commit/f084ebae2106c8d4435dc0385e493fe18c6cec8a)] - fix: cpu usage 100% in node@6.x (#1470) (Yiman Liu <<413893093@qq.com>>)
* [[`8d57216`](http://github.com/cnpm/cnpmjs.org/commit/8d572169b7293a33035257d3525c66b0abb5b679)] - fix: add cache on total (fengmk2 <<fengmk2@gmail.com>>)
* [[`585f55b`](http://github.com/cnpm/cnpmjs.org/commit/585f55bbcc0ce257bfab2f0f545dd8a89c66ca49)] - fix: download url pathname (fengmk2 <<fengmk2@gmail.com>>)
* [[`da2f964`](http://github.com/cnpm/cnpmjs.org/commit/da2f9640b87f1b110210b7b8caaf26b4b854ede8)] - fix: dont override exists weburl (fengmk2 <<fengmk2@gmail.com>>)
* [[`b094f56`](http://github.com/cnpm/cnpmjs.org/commit/b094f5692f83700f152dd6ea9eb65f67385f6b5f)] - fix: changes stream syncer without deps (fengmk2 <<fengmk2@gmail.com>>)
* [[`65bca46`](http://github.com/cnpm/cnpmjs.org/commit/65bca46f3c275bac5dc7497eb266d84605f6f8f8)] - fix: don't cache npm_service.cnpmjs.org request (fengmk2 <<fengmk2@gmail.com>>)
* [[`f9d4858`](http://github.com/cnpm/cnpmjs.org/commit/f9d4858862a4b70cb989c3c60478c2424ca2c139)] - fix: avoid toString as downloads count key (#1438) (fengmk2 <<fengmk2@gmail.com>>)
* [[`8a2f744`](http://github.com/cnpm/cnpmjs.org/commit/8a2f744749fc9f1297ff298fafe14deacf67efea)] - fix: don't update __all__ downloads every times (#1417) (fengmk2 <<fengmk2@gmail.com>>)
* [[`9bdb695`](http://github.com/cnpm/cnpmjs.org/commit/9bdb695375a800464636d70981f433b7a11dd82d)] - fix: proxy to source registry when package is public scoped with encoded path (#1415) (Albert Zhang <<label4king@163.com>>)
* [[`8bd0a2d`](http://github.com/cnpm/cnpmjs.org/commit/8bd0a2d49195734afa988cce69804d8540bbda19)] - fix: swap compress middleware and notFound position (#1413) (alsotang <<alsotang@gmail.com>>)
* [[`93d5def`](http://github.com/cnpm/cnpmjs.org/commit/93d5def8ac8882edbd526e5a7341e07c99463b25)] - fix: show package when non-semver version of semver tag (#1411) (Khaidi Chu <<i@2333.moe>>)
* [[`6a8434e`](http://github.com/cnpm/cnpmjs.org/commit/6a8434e0cae391981579af1a0b533aff0008904f)] - fix: Don't display sync info when the sync mode is none (#1410) (XingKai Zhang <<jack_zhxk@163.com>>)
* [[`4a3a851`](http://github.com/cnpm/cnpmjs.org/commit/4a3a851256483d438753b154d80d28c12c1d625c)] - fix: use <%- instead of <%= in user profile page (#1404) (Khaidi Chu <<i@2333.moe>>)
* [[`3497bae`](http://github.com/cnpm/cnpmjs.org/commit/3497bae2b94237664716911de965a4b27afc083a)] - fix: Obfuscate email address (#1391) (Ankur Kumar <<ankurk91@users.noreply.github.com>>)
* [[`9b8491b`](http://github.com/cnpm/cnpmjs.org/commit/9b8491b736ebcb98df02d26c41334cf7fce306dc)] - fix: use https://cdn.staticfile.org (fengmk2 <<fengmk2@gmail.com>>)
* [[`fc79930`](http://github.com/cnpm/cnpmjs.org/commit/fc799304d8c6710e71364bdf1d1ed0961b9e8695)] - fix: should return `[done] Sync {name}` string when task finished (#1382) (fengmk2 <<fengmk2@gmail.com>>)
* [[`3c20267`](http://github.com/cnpm/cnpmjs.org/commit/3c20267b22491cd2ac2d751ccc459cf1f4fb0f1f)] - fix: don't retry to save log when db error (#1381) (fengmk2 <<fengmk2@gmail.com>>)
* [[`5149aa5`](http://github.com/cnpm/cnpmjs.org/commit/5149aa5a1eb01dfc17f8de1cb6c6abfecca0ed96)] - fix: proxy public package from source registry (#1375) (fengmk2 <<fengmk2@gmail.com>>)
* [[`fc07a38`](http://github.com/cnpm/cnpmjs.org/commit/fc07a38bde81bd93ef9067f3aacb06ae8e76e12b)] - fix: make sure replicate pkg is the latest pkg (#1347) (fengmk2 <<fengmk2@gmail.com>>)
* [[`17f8b66`](http://github.com/cnpm/cnpmjs.org/commit/17f8b6648b2cf8cb4cf17daef2a2477f74a671e8)] - fix: retry from registry when no_db_file error on replicate (fengmk2 <<fengmk2@gmail.com>>)
* [[`d1fe6ce`](http://github.com/cnpm/cnpmjs.org/commit/d1fe6cede7b5a082eabfe9eb94225c9af9399e62)] - fix: add other_urls on download dist tarball (#1345) (fengmk2 <<fengmk2@gmail.com>>)
* [[`8fbad39`](http://github.com/cnpm/cnpmjs.org/commit/8fbad397f3ab7177c6e6c9b458b4b0bf3d24fbd7)] - fix: use rimraf instead of fs.unlink (#1338) (Yiyu He <<dead_horse@qq.com>>)
* [[`0121de3`](http://github.com/cnpm/cnpmjs.org/commit/0121de31a3b7a8da38e31fca4e10d973c07d79e7)] - fix: no need to resync again (#1336) (fengmk2 <<fengmk2@gmail.com>>)
* [[`84a3037`](http://github.com/cnpm/cnpmjs.org/commit/84a3037d90d4b3a316752eda7440ff5c73b0872f)] - fix: avoid query too frequently (#1329) (fengmk2 <<fengmk2@gmail.com>>)
* [[`1f60a01`](http://github.com/cnpm/cnpmjs.org/commit/1f60a0136c5f2e4a33827d1f36b38c49e1e3dec6)] - fix: replicate request error, try to request from official registry (#1316) (fengmk2 <<fengmk2@gmail.com>>)
* [[`6f656a0`](http://github.com/cnpm/cnpmjs.org/commit/6f656a0736c7d1d8b58288ff97590d7cb1317ecd)] - fix: save sync last time when successes > 1000 (fengmk2 <<fengmk2@gmail.com>>)
* [[`1b30146`](http://github.com/cnpm/cnpmjs.org/commit/1b30146e94e7e72f9e762947b1ecdbd176d64532)] - fix: npm >= v5.5.0 login need not `email` (#1275) (#1304) (wmzy <<1256573276@qq.com>>)
* [[`820ae23`](http://github.com/cnpm/cnpmjs.org/commit/820ae23454f0f9755456681f3ced03e634cb3109)] - fix: control sync frequency (fengmk2 <<fengmk2@gmail.com>>)
* [[`bfb29f8`](http://github.com/cnpm/cnpmjs.org/commit/bfb29f82c967cb68f4de3a314200d95a8c59baff)] - fix: use _npmUser reset the maintainers (fengmk2 <<fengmk2@gmail.com>>)
* [[`95aa035`](http://github.com/cnpm/cnpmjs.org/commit/95aa035a275089b50dfc2590497e3bc7319f4f6b)] - fix: make sure maintainers exists on sync worker (liang feng <<anhulife@gmail.com>>)
* [[`6c69a38`](http://github.com/cnpm/cnpmjs.org/commit/6c69a38a508812f0320866d70b555de02e1fc204)] - fix: if replicate error, retry from official registry (#1230) (fengmk2 <<fengmk2@gmail.com>>)
* [[`43ffa99`](http://github.com/cnpm/cnpmjs.org/commit/43ffa995cb8a724e8cd04224c2f137d407bfe014)] - fix: "start" should wait for "stop" to remove the pid file(using Promise) (#1220) (cloudstone <<baby31529@gmail.com>>)
* [[`6c019de`](http://github.com/cnpm/cnpmjs.org/commit/6c019de514c9f4a62db1a1814ca2359408609074)] - fix: changes_stream_syncer log url should not contain sync_upstream=true (fengmk2 <<fengmk2@gmail.com>>)
**others**
* [[`522ad11`](http://github.com/cnpm/cnpmjs.org/commit/522ad11124f168788b28dd925417ae37eb9d3991)] - update readme for now situation (#1506) (alsotang <<alsotang@gmail.com>>)
* [[`0c59791`](http://github.com/cnpm/cnpmjs.org/commit/0c59791e50ef9d3080d5a2ab3e24b5899bd91446)] - Release Release 3.0.0-rc.19 (fengmk2 <<fengmk2@gmail.com>>)
* [[`79fb163`](http://github.com/cnpm/cnpmjs.org/commit/79fb163a3b12f1b9c4c9eafad7f2041e7c4c4dbf)] - chore: README fix typo ( not to use plural for code ) (#1448) (Paul Verest <<enide.github@gmail.com>>)
* [[`be00b65`](http://github.com/cnpm/cnpmjs.org/commit/be00b6557359d328c851e538827d6c681c2c3416)] - refactor: add detail message to error and keep reason (#1445) (alsotang <<alsotang@gmail.com>>)
* [[`f7e9670`](http://github.com/cnpm/cnpmjs.org/commit/f7e9670025c6e7f09d8aa88c676938a2cf4849b5)] - Release Release 3.0.0-rc.14 (fengmk2 <<fengmk2@gmail.com>>)
* [[`d0c3f1b`](http://github.com/cnpm/cnpmjs.org/commit/d0c3f1b19e46e73ce389e78413304a1542811b5f)] - test: shouldjs change from getter to function call (#1420) (alsotang <<alsotang@gmail.com>>)
* [[`d889eba`](http://github.com/cnpm/cnpmjs.org/commit/d889ebafbd6ff1bc15fbf277fd8e143a57e6cac6)] - deps: use agentkeepalive@4 (fengmk2 <<fengmk2@gmail.com>>)
* [[`938a14d`](http://github.com/cnpm/cnpmjs.org/commit/938a14d0a13b711c7b91d795151a7266b0a43c5a)] - chore: Hall of Fame integration on README (#1388) (Gwenael Pluchon <<gwenael.pluchon+github@gmail.com>>)
* [[`26d7147`](http://github.com/cnpm/cnpmjs.org/commit/26d7147562a1ae21db8bfec26983daf311353d96)] - refactor: normalize database structure (#1376) (Khaidi Chu <<i@2333.moe>>)
* [[`5334375`](http://github.com/cnpm/cnpmjs.org/commit/53343751f7c0a34ea0a346172bff0818d27864dd)] - chore: add latest-3 tag (fengmk2 <<fengmk2@gmail.com>>)
3.0.0-alpha.8 / 2017-06-15
==================
* fix: should remove unpublished version on ModuleAbbreviated too (#1192)
* docs: Dockerized cnpmjs.org configuration with installation guide (#1191)
3.0.0-alpha.7 / 2017-06-01
==================
* fix: add missing publish_time property on package list api (#1185)
3.0.0-alpha.6 / 2017-05-18
==================
* feat: add globalHook on config (#1177)
* fix: TypeError caused by invalid engines property (#1151)
* test: add new test for application/vnd.npm.install-v1+json request
3.0.0-alpha.5 / 2017-04-14
==================
* fix: should auto sync missing deprecated property (#1167)
3.0.0-alpha.4 / 2017-04-12
==================
* fix: add missing deprecated on abbreviated meta (#1165)
3.0.0-alpha.2 / 2017-03-27
==================
* fix: only get from package_readme table
3.0.0-alpha.1 / 2017-03-27
==================
* chore: start 3.x
* fix: ignore sync npm registry status 502
* feat: remove readme from package
* feat: [BREAKING_CHANGE] support abbreviated meta
2.19.4 / 2017-03-26
==================
* feat: need to sync sourceNpmRegistry also (#1153)
* docs: change user.json to utf8mb4
2.19.3 / 2017-02-22
==================
* fix: should get package from orginal registry when package is unpublished (#1130)
2.19.2 / 2017-01-05
==================
* fix: should auto sync un-deprecate message (#1105)
2.19.1 / 2016-12-29
==================
* fix: try to use the best repository url (#1102)
2.19.0 / 2016-12-21
==================
* feat: keyword search with limit to support keywords > 100 (#1097)
2.18.0 / 2016-12-05
==================
* fix: support downloads total on scope package (#1088)
* fix: try to sync from official replicate (#1076)
* feat: add change password script (#1070)
* test: skip always fail tests
* test: add node v7
* feat: show more sync info
2.17.2 / 2016-11-13
==================
* fix: ignore long package name on unpublished sync (#1067)
2.17.1 / 2016-11-08
==================
* fix: add publish_time for private packages (#1061)
2.17.0 / 2016-11-03
==================
* feat: make snyk.io url configable (#1058)
2.16.2 / 2016-09-27
==================
* fix: try to use config.registryHost first on setDownloadURL (#1044)
2.16.1 / 2016-08-22
==================
* refactor: refine publishable's code (#1022)
2.16.0 / 2016-08-22
==================
* feat: admin can do everything (#1021)
2.15.0 / 2016-08-22
==================
* feat: return dist-tag on package registry ([#1020](https://github.com/cnpm/cnpmjs.org/issues/1020))
* chore(package): update supertest to version 2.0.0 ([#1004](https://github.com/cnpm/cnpmjs.org/issues/1004))
2.14.0 / 2016-08-04
==================
* feat: password may contains ":" ([#999](https://github.com/cnpm/cnpmjs.org/issues/999))
* fix: limit sync fails email notice ([#1006](https://github.com/cnpm/cnpmjs.org/issues/1006))
2.13.0 / 2016-07-26
==================
* feat: enable maxrequests middleware ([#1003](https://github.com/cnpm/cnpmjs.org/issues/1003))
2.12.2 / 2016-07-11
==================
* fix: getModuleByRange don't list all packages ([#990](https://github.com/cnpm/cnpmjs.org/issues/990))
* fix: should show new version package count ([#984](https://github.com/cnpm/cnpmjs.org/issues/984))
2.12.1 / 2016-07-01
==================
* fix: make sure chagnes stream destroy ([#982](https://github.com/cnpm/cnpmjs.org/issues/982))
* chore(package): update semver to version 5.2.0 ([#978](https://github.com/cnpm/cnpmjs.org/issues/978))
* deps: use ^ instead of ~ ([#976](https://github.com/cnpm/cnpmjs.org/issues/976))
* chore(package): update mini-logger to version 1.1.1 ([#973](https://github.com/cnpm/cnpmjs.org/issues/973))
2.12.0 / 2016-06-26
==================
* fix: logger seperator should be one EOL ([#972](https://github.com/cnpm/cnpmjs.org/issues/972))
* feat: add security check badge for public package ([#971](https://github.com/cnpm/cnpmjs.org/issues/971))
2.11.0 / 2016-06-25
==================
* feat: add changes stream syncer ([#970](https://github.com/cnpm/cnpmjs.org/issues/970))
* chore(package): update pg to version 5.1.0 ([#953](https://github.com/cnpm/cnpmjs.org/issues/953))
2.10.1 / 2016-06-05
==================
* fix: should sync missing public scoped package on install ([#946](https://github.com/cnpm/cnpmjs.org/issues/946))
* chore(package): update bytes to version 2.4.0 ([#943](https://github.com/cnpm/cnpmjs.org/issues/943))
* userService ([#926](https://github.com/cnpm/cnpmjs.org/issues/926))
* chore(package): update should to version 8.4.0 ([#928](https://github.com/cnpm/cnpmjs.org/issues/928))
* chore(package): update humanize-ms to version 1.2.0 ([#927](https://github.com/cnpm/cnpmjs.org/issues/927))
* chore(package): update kcors to version 1.2.1 ([#918](https://github.com/cnpm/cnpmjs.org/issues/918))
* chore(package): update urllib to version 2.9.0 ([#898](https://github.com/cnpm/cnpmjs.org/issues/898))
2.10.0 / 2016-04-15
==================
* feat: show tarball url on package page ([#894](https://github.com/cnpm/cnpmjs.org/issues/894))
* chore(package): update koa-mock to version 1.6.1 ([#891](https://github.com/cnpm/cnpmjs.org/issues/891))
2.9.5 / 2016-04-12
==================
* fix: change logo url to a better https source
* fix: http://cnpmjs.org/package/fms pre style ([#739](https://github.com/cnpm/cnpmjs.org/issues/739))
2.9.4 / 2016-04-09
==================
* fix: don't sync constructor package on exists mode ([#883](https://github.com/cnpm/cnpmjs.org/issues/883))
* Update utility to version 1.7.0 🚀
* chore: update sponsor link
2.9.3 / 2016-04-05
==================
* fix: use better diff time to check sync status
* Update sequelize to version 3.21.0 🚀
* chore(package): update agentkeepalive to version 2.1.0
* chore(package): update pg to version 4.5.2
2.9.2 / 2016-03-29
==================
* fix: override antd for ul & ol list number & icon.
2.9.1 / 2016-03-29
==================
* refactor: add more ua info on syncer
2.9.0 / 2016-03-26
==================
* feat: only admin can unpublish
* chore(package): update gravatar to version 1.5.0
* chore(package): update sequelize to version 3.20.0
* fix: fix save download count unqiue constraint error
* chore(package): update moment to version 2.12.0
2.8.1 / 2016-03-07
==================
* fix: only send warning email if no any sync data after 24h
* chore(package): update kcors to version 1.1.0
* chore(package): update koa to version 1.2.0
* chore(package): update urllib to version 2.8.0
2.8.0 / 2016-02-23
==================
* fix: convert `*` to latest tag
* deps: upgrade deps and remove node 2.0.0 support
* doc: update sponsors on readme
* fix: update copyright year
* doc: fix disturl typo
* deps: sequelize@3.19.0
2.7.1 / 2016-02-01
==================
* fix(semver): when have invalid version([#817](https://github.com/cnpm/cnpmjs.org/issues/817))
2.7.0 / 2016-02-01
==================
* test: fix all test cases
* test: fix unpublish
* test: add complex range test case
* feat: support semver([#816](https://github.com/cnpm/cnpmjs.org/issues/816))
2.6.2 / 2016-01-19
==================
* feat: list & show support jsonp
* chore(package): update urllib to version 2.7.0
* Delete install.md
2.6.1 / 2016-01-12
==================
* fix: source registry is not cnpm, ignore check status
2.6.0 / 2016-01-12
==================
* feat(sync): monitor sync status
* chore(package): update agentkeepalive to version 2.0.3
* fix SequelizeDatabaseError: ER_NO_SUCH_TABLE: Table 'qnpm.total' doesn't exist\nreproduce this bug:\nthe first startup of cnpmjs.org
* chore(package): update moment to version 2.11.0
* chore(package): update xss to version 0.2.10
* chore(package): update pg-hstore to version 2.3.2
* chore(package): update mini-logger to version 1.1.0
* chore(package): update urllib to version 2.6.0
* fix: row.package will json parse error
* remove bluebird
* chore(package): update utility to version 1.6.0
2.5.1 / 2015-12-02
==================
* chore(package): update bluebird to version 3.0.6
* fix: SequelizeDatabaseError
* fix(dist_tag): disable delete latest tag
* feat: count total private pkgs
* fix: use isoweek. a week start from monday
* chore(package): update xss to version 0.2.8
* chore(package): update semver to version 5.1.0
2.5.0 / 2015-11-17
==================
* test: add node v5
* feat(sync): sync deleted user
* Update show.js
* chore(package): update bytes to version 2.2.0
* do not sync inner username
* gzip static file
* chore(package): update bytes to version 2.1.0
* chore(package): update is-type-of to version 1.0.0
* Update static.js
* chore(package): update cfork to version 1.4.0
* chore(package): update bluebird to version 3.0.5
2.4.1 / 2015-10-27
==================
* fix: improve registry index page performance with cache
* Configable badge URL prefix.
* chore(package): update koa-mock to version 1.5.0
* chore(package): update urllib to version 2.5.0
* chore(package): update co to version 4.6.0
* chore(package): update semver to version 5.0.3
2.4.0 / 2015-10-21
==================
* feat(registry): add package's dependents api
* fix: show package's dependents
* chore(package): update utility to version 1.5.0
* chore(package): update bluebird to version 2.10.2
2.3.1 / 2015-10-15
==================
* refactor: remove gnode
* deps: upgrade giturl
* Some fixes
2.3.0 / 2015-10-15
==================
* Add dev dependencies.
* Package page fix.
* refactor: add more sync log
* Add mock data.
* refactor: add more sync log
* Fix sidebar overflow.
* Merge pull request [#680](https://github.com/cnpm/cnpmjs.org/issues/680) from ibigbug/ant-design
* Clean code.
* Indent.
* chore(package): update debug to version 2.2.0
* chore(package): update should to version 7.1.0
* chore(package): update koa to version 1.1.0
* Remove default adBanner.
* Package pages.
* Common styles.
* search page.
* Sync page.
* Profile page antd style.
* Unpublished pkg page style.
* Add page title for unpubed pkg.
* Index page style use antd.
* chore(package): update pg to version 4.4.2
* chore(package): update cfork to version 1.3.1
* chore(package): update moment to version 2.10.6
* feat(badge): Use qiniu cdn
2.2.1 / 2015-09-30
==================
* test: use istanbul
* pref: move out try/catch block
* fix: support nfs.url is generator
2.2.0 / 2015-09-29
==================
* feat: list packages by username
* test: use codecov
* feat(badge): support custom subject
* fix(sync): add recover logic
* feat(sync): add sync scripts
2.1.5 / 2015-09-05
==================
* fix: only sync update packages
2.1.4 / 2015-09-05
==================
* fix: support new array and old map format both
* fix: /-/all/since had been redirect to /-/all/static/today.json
* fix(list): let koa-etag to caculate the etag
2.1.3 / 2015-08-18
==================
* fix: sync public scope package download url is wrong
* fix: default registry change to taobao registry
2.1.2 / 2015-08-09
==================
* fix(syncer): sync worker pkg null bug
* feat(web): add downloads badge
2.1.1 / 2015-07-27
==================
* fix: fix private scope package detect
* fix: dont sync if upstream is npm registry
* fix(sync): support sync public scope package
* test: fix fails tests
* fix: ignore 503 server error
* fix: ignore sync 503 server error
2.1.0 / 2015-07-08
==================
* feat(web): search support jsonp
* fix function name
2.0.0 / 2015-05-11
==================
* fix: real download as stream
* add custom ad banner config
* add sponsors: ucloud.cn
* fix small typo
* feat(urllib): support http_proxy
* force using https links
2.0.0-rc.15 / 2015-02-15
==================
* fix(markdown): filter xss after markdown render
* feat(database): support PostgreSQL
2.0.0-rc.14 / 2015-02-14
==================
* feat: support always-auth
* fix mysql select args = [] bug
* fix [#597](https://github.com/cnpm/cnpmjs.org/issues/597) sequelize raw query.
* fix(markdown): hotfix markdown-it cpu problem
* feat: upgrade to co4
* use kcors fixes [#594](https://github.com/cnpm/cnpmjs.org/issues/594)
2.0.0-rc.13 / 2015-02-04
==================
* docs: Deploy a private npm registry in 5 minutes
* refactor(config): move application data to ~/.cnpmjs.org/
* fix(sync): make get popular pakcage faster
* feat(sync): web page also redirect to npm www
* refactor(config): make syncModel to none by default
* test: fix admin can not publish non-scoped package test cases
* docs: add chinese mirror link
* fix: admin can not publish non scoped package on "none" sync model
* feat(sync): enable none syncModel proxy all public packages
* fix: ignore username start with " or '
* fix(bin): fix stop not work on iojs
2.0.0-rc.12 / 2015-02-01
==================
* feat(syncer): add hostname ua
* fix(web): remove pkg.contributors logic
2.0.0-rc.11 / 2015-02-01
==================
* fix xss tests
* fix(markdown): revert marky-markdown
2.0.0-rc.10 / 2015-01-31
==================
* feat(middleware): CORS headers for GET and HEAD requests
* fix(readme): fix index page markdown
* feat(markdown): use npm same markdown parser
* feat(download): support download redirect to nfs
* feat(syncer): request npm registry with gzip
* change(sync): remove dist syncer
* feat(registry): add dist tag api
* refactor(common): remove redis store
2.0.0-rc.9 / 2015-01-22
==================
* hotfix reame render error, pin xss
* fix registry user auth api
2.0.0-rc.8 / 2015-01-10
==================
* fix(markdown): readme.md allow scripts
* fix(style) flexbox compatibility for both chrome and firefox (@afc163)
* feat(sync): default sync exist packages
2.0.0-rc.7 / 2015-01-07
==================
* install sync dont check `enablePrivate`
* fix(markdown): filter xss readme before markdown render
2.0.0-rc.6 / 2015-01-05
==================
* fix(markdown): use markdown-it
* add userService options on config
* add upload to nfs sync info log
2.0.0-rc.5 / 2015-01-03
==================
* fix(markdown): use marked instead of remarkable
* fix(package): pkg.readme is not a string, dont remarkable it
* feat(sync): sync user profile
2.0.0-rc.4 / 2014-12-25
==================
* refactor(download): try to use nsf.url() first
* use __all__ for full downloads
* refactor(download_total): optimize download total
* fix sqlite raw sql return datetime is string format
* fix(download_total): change column date to DateTime
* fix(services/download_total): fix download_total slow sql on `date >= $start and date <= $end`
* fix(markdown): replace marked use remarkable
2.0.0-rc.3 / 2014-12-14
==================
* fix(services): need to detect instance isDirty or not before save()
2.0.0-rc.2 / 2014-12-11
==================
* add download API, closes [#529](https://github.com/cnpm/cnpmjs.org/issues/529)
* fix missing home page title (@rockdai)
* Fix typo in view/web/package.html (@LoicMahieu)
2.0.0-rc.1 / 2014-12-09
==================
* fix xss on title
* feat(badge): support badge image url with tag
2.0.0-beta5 / 2014-12-05
==================
* hotfix package.html typo. Closes #521
* hotfix package.html typo. Closes [#521](https://github.com/cnpm/cnpmjs.org/issues/521)
* Add editorconfig
* fix(web/package): package name to long cause style problem fix
* fix(css): use github-markdown-css for markdown body
@@ -14,7 +660,7 @@
* fix(registry): add missing /-/short api
* zoom sync link
* new design for package page
* image max width, fixed #505
* image max width, fixed [#505](https://github.com/cnpm/cnpmjs.org/issues/505)
* feat(middleware): block Ruby user-agent
2.0.0-beta3 / 2014-11-12
@@ -44,7 +690,7 @@
2.0.0-beta0 / 2014-11-02
==================
* ungrade koa-markdown to use remarkable, close #482
* ungrade koa-markdown to use remarkable, close [#482](https://github.com/cnpm/cnpmjs.org/issues/482)
* fix(module_log): limit module sync log size to 1MB
* refactor(config): remove adaptScope config key
* chore(Makefile): $ make install-production
@@ -507,7 +1153,7 @@
* npm publish dont contains .jshint*
* npm test run jshint
* Add jshint check: $ make jshint
* use `yield* next` instead of `yield next`
* use `yield next` instead of `yield next`
* replace dist.u.qiniudn.com with cnpmjs.org/dist
0.3.5 / 2014-03-05

View File

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

View File

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

173
README.md
View File

@@ -1,89 +1,77 @@
cnpmjs.org
=======
[![NPM version][npm-image]][npm-url]
[![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]
[![Test coverage][codecov-image]][codecov-url]
[![Known Vulnerabilities][snyk-image]][snyk-url]
[![npm download][download-image]][download-url]
[![gitter][gitter-image]][gitter-url]
[npm-image]: http://cnpmjs.org/badge/v/cnpmjs.org.svg?style=flat-square
[npm-url]: http://cnpmjs.org/package/cnpmjs.org
[travis-image]: https://img.shields.io/travis/cnpm/cnpmjs.org.svg?style=flat-square
[travis-url]: https://travis-ci.org/cnpm/cnpmjs.org
[coveralls-image]: https://img.shields.io/coveralls/cnpm/cnpmjs.org.svg?style=flat-square
[coveralls-url]: https://coveralls.io/r/cnpm/cnpmjs.org?branch=master
[gittip-image]: https://img.shields.io/gittip/fengmk2.svg?style=flat-square
[gittip-url]: https://www.gittip.com/fengmk2/
[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/
[codecov-image]: https://codecov.io/gh/cnpm/cnpmjs.org/branch/master/graph/badge.svg
[codecov-url]: https://codecov.io/gh/cnpm/cnpmjs.org
[snyk-image]: https://snyk.io/test/npm/cnpmjs.org/badge.svg?style=flat-square
[snyk-url]: https://snyk.io/test/npm/cnpmjs.org
[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?
## Description
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).
Our goal is to provide a low cost maintenance and easy to use solution for private npm.
Our goal is to provide a low cost maintenance, easy to use, and easy to scale solution for private npm.
## What can you do with `cnpmjs.org`
## 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.
* Build a npm mirror. (we use it to build a mirror in China: [https://npm.taobao.org/](https://npm.taobao.org/))
* Use the private npm service provided by Alibaba Cloud DevOps which build with cnpm. [https://packages.aliyun.com/](https://packages.aliyun.com/?channel=pd_cnpm_github)
### Features
## Features
* **Support "scoped" packages**: [npm/npm#5239](https://github.com/npm/npm/issues/5239)
* **Support [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing)**
* **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.
* **Low cost and easy maintenance**: `package.json` info can store in MySQL, MariaDB, SQLite or PostgreSQL.
tarball(tgz file) can store in Amazon S3 or other object storage service.
* **Automatic synchronization**: automatically sync from any registry specified. support two sync modes:
- Sync all modules from upstream
- Only sync the modules after first access.
* **Manual synchronization**: automatic synchronization may has little delay. you can sync manually on web page.
* **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)
And it is easy to wrap for your own registry which build with `cnpmjs.org`.
* **Compatible with npm client**: you can use the official npm client with `cnpmjs.org`.
you only need to change the registry in client config.
* **Support http_proxy**: if you're behind a firewall, you can provide a http proxy for cnpmjs.org.
**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).
## Docs
## 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)
* [How to deploy](https://github.com/cnpm/cnpmjs.org/wiki/Deploy)
* 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)
* [Sync packages through `http_proxy`](https://github.com/cnpm/cnpmjs.org/wiki/Sync-packages-through-http_proxy)
* [Migrating from 1.x to 2.x](https://github.com/cnpm/cnpmjs.org/wiki/Migrating-from-1.x-to-2.x)
* [New features in 2.x](https://github.com/cnpm/cnpmjs.org/wiki/New-features-in-2.x).
* [wiki](https://github.com/cnpm/cnpmjs.org/wiki)
## Develop on your local machine
### Dependencies
* [node](http://nodejs.org) >=0.11.12, use `--harmony`
* [node](http://nodejs.org) >= 8.0.0
* 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`.
* [MySQL](http://dev.mysql.com/downloads/) >= 5.6.16, include `mysqld` and `mysql cli`. I test on `mysql@5.6.16`.
* MariaDB
* PostgreSQL
### Clone codes and run test
### Clone code and run test
```bash
# clone from git
@@ -98,13 +86,68 @@ $ make test
# coverage
$ make test-cov
# udpate dependencies
# update dependencies
$ make autod
# start server with development mode
$ make dev
```
### Dockerized cnpmjs.org Installation Guide
Cnpmjs.org shipped with a simple but pragmatic Docker Compose configuration.With the configuration, you can set up a MySQL backend cnpmjs.org instance by executing just one command on Docker installed environment.
#### Preparation
* [Install Docker](https://www.docker.com/community-edition)
* [Install Docker Compose](https://docs.docker.com/compose/install/) (Docker for Mac, Docker for Windows include Docker Compose, so most Mac and Windows users do not need to install Docker Compose separately)
* (Optional) Speed up Docker images downloading by setting up [Docker images download accelerator](https://yq.aliyun.com/articles/29941)
#### Dockerized cnpmjs.org control command
Make sure your current working directory is the root of this GitHub repository.
##### Run dockerized cnpmjs.org
```bash
$docker-compose up
```
This command will build a Docker image using the current code of repository. Then set up a dockerized MySQL instance with data initialized. After Docker container running, you can access your cnpmjs.org web portal at http://127.0.0.1:7002 and npm register at http://127.0.0.1:7001.
#### Run cnpmjs.org in the backend
```bash
$docker-compose up -d
```
#### Rebuild cnpmjs.org Docker image
```bash
$docker-compose build
```
#### Remove current dockerized cnpmjs.org instance
The current configuration set 2 named Docker Volume for your persistent data. If you haven't change the repository directory name, them will be "cnpmjsorg_cnpm-files-volume" & "cnpmjsorg_cnpm-db-volume".
Be Careful, the following commands will remove them.
```bash
$docker-compose rm
$docker volume rm cnpmjsorg_cnpm-files-volume
$docker volume rm cnpmjsorg_cnpm-db-volume
```
You can get more information about your data volumes using the below commands:
```bash
$docker volume ls // list all of your Docker volume
$docker volume inspect cnpmjsorg_cnpm-files-volume
$docker volume inspect cnpmjsorg_cnpm-db-volume
```
## How to contribute
* Clone the project
@@ -114,27 +157,23 @@ $ make dev
Tips: make sure your code is following the [node-style-guide](https://github.com/felixge/node-style-guide).
## Top contributors
[![0](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/images/0)](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/0)
[![1](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/images/1)](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/1)
[![2](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/images/2)](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/2)
[![3](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/images/3)](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/3)
[![4](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/images/4)](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/4)
[![5](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/images/5)](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/5)
[![6](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/images/6)](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/6)
[![7](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/images/7)](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/7)
## Sponsors
- [![阿里云](https://static.aliyun.com/images/www-summerwind/logo.gif)](http://click.aliyun.com/m/4288/) [![阿里云云效](https://img.alicdn.com/tfs/TB116yt3fb2gK0jSZK9XXaEgFXa-106-20.png)](https://devops.aliyun.com/?channel=pd_cnpm_github) (2016.2 - now)
## License
(The MIT License)
Copyright(c) cnpmjs.org and other contributors.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
[MIT](LICENSE.txt)

View File

@@ -1,142 +0,0 @@
/**!
* cnpmjs.org - backup/dump.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
/**
* 1. dump module
* 2. dump tag
* 3. dump user
* 4. total
* 5. download_total
*/
var path = require('path');
var fs = require('fs');
var moment = require('moment');
var eventproxy = require('eventproxy');
var util = require('util');
var zlib = require('zlib');
var mysql = require('../common/mysql');
var nfs = require('../common/nfs');
var config = require('../config');
function dumpTable(name, lastRow, callback) {
var sql = 'SELECT * from ' + name + ' WHERE gmt_modified >=? ORDER BY gmt_modified ASC LIMIT 10000;';
mysql.query(sql, [lastRow.gmt_modified], function (err, rows) {
if (err || rows.length === 0) {
return callback(err, rows);
}
if (rows[0].id === lastRow.id) {
rows = rows.slice(1);
}
callback(null, rows);
});
}
function log() {
var str = '[' + moment().format('YYYY-MM-DD HH:mm:ss') + '] ' + util.format.apply(util, arguments);
console.log(str);
}
function syncTable(name, callback) {
var datadir = __dirname;
var dataFile = path.join(datadir, moment().format('YYYY-MM-DD-HH') + '_' + name + '.json');
var lastRowFile = path.join(datadir, name + '_lastdate.json');
var lastRow = null;
if (fs.existsSync(lastRowFile)) {
lastRow = require(lastRowFile);
lastRow.gmt_modified = new Date(Date.parse(lastRow.gmt_modified));
} else {
lastRow = {
gmt_modified: new Date('2011-11-11'),
};
}
log('getting "%s" since %j', name, lastRow);
dumpTable(name, lastRow, function (err, rows) {
console.log('[%s] got %d rows', Date(), rows && rows.length || 0);
if (err) {
return callback(err);
}
if (!rows || rows.length === 0) {
return callback();
}
var writeStream = fs.createWriteStream(dataFile, {flags: 'a'});
writeStream.once('error', callback);
for (var i = 0; i < rows.length; i++) {
writeStream.write(JSON.stringify(rows[i]) + '\n');
}
writeStream.end();
writeStream.on('finish', function () {
log('append %d rows to %s', rows.length, dataFile);
var gzfile = dataFile + '.gz';
var gzip = zlib.createGzip();
var inp = fs.createReadStream(dataFile);
var out = fs.createWriteStream(gzfile);
inp.pipe(gzip).pipe(out);
out.once('error', callback);
out.on('finish', function () {
var key = path.join(config.backupFilePrefix, path.basename(gzfile));
log('saving %s to %s ...', gzfile, key);
nfs.upload(gzfile, {key: key}, function (err, result) {
if (err) {
return callback(err);
}
lastRow = rows[rows.length - 1];
lastRow = {gmt_modified: lastRow.gmt_modified, id: lastRow.id};
fs.writeFileSync(lastRowFile, JSON.stringify(lastRow));
log('save %s data file to %j, lastrow: %j', name, result, lastRow);
callback();
});
});
});
});
}
var ep = eventproxy.create();
ep.fail(function (err) {
log('error: %s', err.stack);
process.exit(1);
});
syncTable('module', ep.done('module'));
ep.on('module', function () {
syncTable('tag', ep.done('tag'));
});
ep.on('tag', function () {
syncTable('user', ep.done('user'));
});
ep.on('user', function () {
syncTable('total', ep.done('total'));
});
ep.on('total', function () {
syncTable('download_total', ep.done('download_total'));
});
ep.on('download_total', function () {
ep.emit('finish');
});
ep.on('finish', function () {
log('finish, %d process exit', process.pid);
process.exit(0);
});

24
bin/change_password.js Normal file
View File

@@ -0,0 +1,24 @@
"use strict";
// Only support for ./services/DefaultUserService. If you use custom user service, ignore this file.
// call with:
// $ node ./bin/change_password.js 'username' 'new_password'
var UserModel = require('../models').User;
var co = require('co');
var utility = require('utility');
var username = process.argv[2];
var newPassword = process.argv[3];
co(function * () {
var user = yield UserModel.find({where: {name: username}});
var salt = user.salt;
console.log(`user original password_sha: ${user.password_sha}`);
var newPasswordSha = utility.sha1(newPassword + salt);
user.password_sha = newPasswordSha;
user = yield user.save();
console.log(`change user password successful!! user new password_sha: ${user.password_sha}`);
process.exit(0);
}).catch(function (e) {
console.log(e);
});

138
bin/cli.js Executable file
View File

@@ -0,0 +1,138 @@
#!/usr/bin/env node
/**!
* Copyright(c) cnpm and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <m@fengmk2.com> (http://fengmk2.com)
*/
'use strict';
/**
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:cli');
var program = require('commander');
var path = require('path');
var fs = require('fs');
var mkdirp = require('mkdirp');
var treekill = require('treekill');
var version = require('../package.json').version;
function list(val) {
return val.split(',');
}
program
.version(version);
program
.command('start')
.description('start cnpmjs.org server')
.option('--admins <admins>', 'set admins', list)
.option('--scopes <scopes>', 'set scopes', list)
// .option('--cluster', 'enable cluster mode')
.option('--dataDir <dataDir>', 'cnpmjs.org data dir, default is `$HOME/.cnpmjs.org`')
.action(start);
program
.command('stop')
.description('stop cnpmjs.org server')
.option('--dataDir <dataDir>', 'cnpmjs.org data dir, default is `$HOME/.cnpmjs.org`')
.action(stop);
program.parse(process.argv);
function start(options) {
stop(options)
// wait for "stop" method to remove the pid file
.then(function () {
var dataDir = options.dataDir || path.join(process.env.HOME, '.cnpmjs.org');
mkdirp.sync(dataDir);
var configfile = path.join(dataDir, 'config.json');
var config = {};
if (fs.existsSync(configfile)) {
try {
config = require(configfile);
} catch (err) {
console.warn('load old %s error: %s', configfile, err);
}
}
// config.enableCluster = !!options.cluster;
if (options.admins) {
config.admins = {};
for (var i = 0; i < options.admins.length; i++) {
config.admins[options.admins[i]] = options.admins[i] + '@localhost.com';
}
}
if (options.scopes) {
config.scopes = options.scopes.map(function (name) {
if (name[0] !== '@') {
name = '@' + name;
}
return name;
});
}
var configJSON = JSON.stringify(config, null, 2);
fs.writeFileSync(configfile, configJSON);
debug('save config %s to %s', configJSON, configfile);
// if sqlite db file not exists, init first
initDatabase(function() {
require('../dispatch');
});
fs.writeFileSync(path.join(dataDir, 'pid'), process.pid + '');
});
}
function stop(options) {
var dataDir = options.dataDir || path.join(process.env.HOME, '.cnpmjs.org');
var pidfile = path.join(dataDir, 'pid');
return new Promise(function (resolve) {
if (!fs.existsSync(pidfile)) {
resolve();
return;
}
var pid = Number(fs.readFileSync(pidfile, 'utf8'));
treekill(pid, function (err) {
if (err) {
console.log(err);
throw err;
}
console.log('cnpmjs.org server:%d stop', pid);
fs.unlinkSync(pidfile);
resolve();
});
});
}
function initDatabase(callback) {
var models = require('../models');
models.sequelize.sync({ force: false })
.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] `sqlite` sequelize sync and init success');
callback();
}
});
})
.catch(function (err) {
console.error('[models/init_script.js] sequelize sync fail');
console.error(err);
throw err;
});
}

View File

@@ -7,7 +7,7 @@ export NODE_ENV='production'
ulimit -c unlimited
cd `dirname $0`/..
NODEJS='node --harmony'
NODEJS='node'
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'));\
@@ -52,9 +52,9 @@ stop()
kill -15 $PID
sleep 2
node_num=`ps -ef |grep node|grep ${PROJECT_NAME}|grep -v grep|wc -l`
node_num=`ps -ef | grep ${PROJECT_NAME} | grep -v grep | wc -l`
if [ $node_num != 0 ]; then
ps -ef |grep node | grep ${PROJECT_NAME} |grep -v grep|awk '{print $2}'|xargs kill -9
ps -ef | grep ${PROJECT_NAME} |grep -v grep|awk '{print $2}'|xargs kill -9
ipcs -s | grep 0x | awk '{print $2}' | xargs -n1 ipcrm -s > /dev/null 2>&1
ipcs -m | grep 0x | awk '{print $2}' | xargs -n1 ipcrm -m > /dev/null 2>&1
fi

View File

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

16
common/cache.js Normal file
View File

@@ -0,0 +1,16 @@
'use strict';
const debug = require('debug')('cnpmjs.org:cache');
const Redis = require('ioredis');
const config = require('../config');
let client;
if (config.redisCache.enable) {
client = new Redis(config.redisCache.connectOptions);
client.on('ready', () => {
debug('connect ready, getBuiltinCommands: %j', client.getBuiltinCommands());
});
}
module.exports = client;

View File

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

View File

@@ -1,23 +1,9 @@
/**!
* cnpmjs.org - common/mail.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 mailConfig = require('../config').mail;
var nodemailer = require('nodemailer');
var utility = require('utility');
var os = require('os');
var mailConfig = require('../config').mail;
var smtpConfig;
if (mailConfig.auth) {
@@ -25,6 +11,7 @@ if (mailConfig.auth) {
smtpConfig = mailConfig;
} else {
smtpConfig = {
enable: mailConfig.enable,
// backward compat
host: mailConfig.host,
port: mailConfig.port,
@@ -37,7 +24,7 @@ if (mailConfig.auth) {
};
}
var transport = nodemailer.createTransport(smtpConfig);
var transport;
/**
* Send notice email with mail level and appname.
@@ -71,6 +58,15 @@ LEVELS.forEach(function (level) {
exports.send = function (to, subject, html, callback) {
callback = callback || utility.noop;
if (mailConfig.enable === false) {
console.log('[send mail debug] [%s] to: %s, subject: %s\n%s', Date(), to, subject, html);
return callback();
}
if (!transport) {
transport = nodemailer.createTransport(smtpConfig);
}
var message = {
from: mailConfig.from || mailConfig.sender,
to: to,

20
common/markdown.js Normal file
View File

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

View File

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

View File

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

View File

@@ -1,19 +1,5 @@
/**!
* 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');
@@ -35,6 +21,7 @@ if (config.mysqlServers && config.database.dialect === 'sqlite') {
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];
var dialectOptions = config.database && config.database.dialectOptions;
config.database = {
db: config.mysqlDatabase,
username: server.user,
@@ -45,10 +32,13 @@ if (config.mysqlServers && config.database.dialect === 'sqlite') {
pool: {
maxConnections: config.mysqlMaxConnections || 10,
minConnections: 0,
maxIdleTime: 30000
maxIdleTime: 30000,
},
logging: !!process.env.SQL_DEBUG,
};
if (dialectOptions) {
config.database.dialectOptions = dialectOptions;
}
}
var database = config.database;

View File

@@ -1,35 +1,84 @@
/**!
* 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 urlparse = require('url').parse;
var urllib = require('urllib');
var HttpAgent = require('agentkeepalive');
var HttpsAgent = require('agentkeepalive').HttpsAgent;
var config = require('../config');
var url = require('url');
var URL = require('url').URL;
var httpAgent;
var httpsAgent;
if (config.httpProxy) {
var tunnel = require('tunnel-agent');
var urlinfo = urlparse(config.httpProxy);
if (urlinfo.protocol === 'http:') {
httpAgent = tunnel.httpOverHttp({
proxy: {
host: urlinfo.hostname,
port: urlinfo.port
}
});
httpsAgent = tunnel.httpsOverHttp({
proxy: {
host: urlinfo.hostname,
port: urlinfo.port
}
});
} else if (urlinfo.protocol === 'https:') {
httpAgent = tunnel.httpOverHttps({
proxy: {
host: urlinfo.hostname,
port: urlinfo.port
}
});
httpsAgent = tunnel.httpsOverHttps({
proxy: {
host: urlinfo.hostname,
port: urlinfo.port
}
});
} else {
throw new TypeError('httpProxy format error: ' + config.httpProxy);
}
} else {
httpAgent = new HttpAgent({
timeout: 0,
keepAliveTimeout: 15000
});
httpsAgent = new HttpsAgent({
timeout: 0,
keepAliveTimeout: 15000
});
}
var httpAgent = new HttpAgent({
timeout: 0,
keepAliveTimeout: 15000
});
var httpsAgent = new HttpsAgent({
timeout: 0,
keepAliveTimeout: 15000
});
var client = urllib.create({
agent: httpAgent,
httpsAgent: httpsAgent
});
var request = urllib.HttpClient.prototype.request;
function getAccelerateUrl(url) {
const urlObj = typeof url === 'string' ? new URL(url) : url;
const newHost = config.accelerateHostMap && config.accelerateHostMap[urlObj.host];
if (newHost) {
urlObj.host = newHost;
}
return urlObj.toString();
}
client.request = function (requestUrl, options) {
const accelerateUrl = getAccelerateUrl(requestUrl);
options = Object.assign({}, options, {
formatRedirectUrl: function (from, to) {
return getAccelerateUrl(url.resolve(from, to));
}
});
return Reflect.apply(request, client, [ accelerateUrl, options ]);
};
module.exports = client;
module.exports.USER_AGENT = urllib.USER_AGENT;

View File

@@ -1,32 +1,25 @@
/**!
* cnpmjs.org - config/index.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 mkdirp = require('mkdirp');
var copy = require('copy-to');
var path = require('path');
var fs = require('fs');
var os = require('os');
var utility = require('utility');
var version = require('../package.json').version;
var Nfs = require('fs-cnpm');
var root = path.dirname(__dirname);
var dataDir = path.join(process.env.HOME || root, '.cnpmjs.org');
var config = {
version: version,
dataDir: dataDir,
// overriding length should alter database table length
versionLen: 70, // semver max length
nameLen: 214, // name max length
tagLen: 70, // tag name max length
/**
* Cluster mode
@@ -37,25 +30,48 @@ var config = {
/*
* server configure
*/
registryPort: 7001,
webPort: 7002,
bindingHost: '127.0.0.1', // only binding on 127.0.0.1 for local access
// default is ctx.protocol
protocol: '',
// When sync package, cnpm not know the access protocol.
// So should set manually
backupProtocol: 'http',
// debug mode
// if in debug mode, some middleware like limit wont load
// logger module will print to stdout
debug: true,
debug: process.env.NODE_ENV === 'development',
// page mode, enable on development env
pagemock: process.env.NODE_ENV === 'development',
// session secret
sessionSecret: 'cnpmjs.org test session secret',
// max request json body size
jsonLimit: '10mb',
// log dir name
logdir: path.join(root, '.tmp/logs'),
logdir: path.join(dataDir, 'logs'),
// update file template dir
uploadDir: path.join(root, '.dist'),
uploadDir: path.join(dataDir, 'downloads'),
// web page viewCache
viewCache: false,
// registry http response cache control header
// if you are using CDN, can set it to 'max-age=0, s-maxage=10, must-revalidate'
// it meaning cache 10s on CDN server and no cache on client side.
registryCacheControlHeader: '',
// if you are using CDN, can set it to 'Accept, Accept-Encoding'
registryVaryHeader: '',
// disable package search
disableSearch: false,
// view files directory
viewDir: path.join(root, 'view', 'web'),
customRegistryMiddlewares: [],
customWebMiddlewares: [],
// config for koa-limit middleware
// for limit download rates
limit: {
@@ -81,6 +97,7 @@ var config = {
// email notification for errors
// check https://github.com/andris9/Nodemailer for more informations
mail: {
enable: false,
appname: 'cnpmjs.org',
from: 'cnpmjs.org mail sender <adderss@gmail.com>',
service: 'gmail',
@@ -89,20 +106,10 @@ var config = {
pass: 'your password'
}
},
// forward Compat with old style
// mail: {
// appname: 'cnpmjs.org',
// sender: 'cnpmjs.org mail sender <adderss@gmail.com>',
// host: 'smtp.gmail.com',
// port: 465,
// user: 'address@gmail.com',
// pass: 'your password',
// ssl: true,
// debug: false
// },
logoURL: '//ww4.sinaimg.cn/large/69c1d4acgw1ebfly5kjlij208202oglr.jpg', // cnpm logo image url
logoURL: 'https://os.alipayobjects.com/rmsportal/oygxuIUkkrRccUz.jpg', // cnpm logo image url
adBanner: '',
customHeader: '',
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`
@@ -118,7 +125,7 @@ var config = {
*/
database: {
db: 'cnpmjs_test',
db: 'cnpmjs',
username: 'root',
password: '',
@@ -140,21 +147,35 @@ var config = {
maxIdleTime: 30000
},
dialectOptions: {
// if your server run on full cpu load, please set trace to false
trace: true,
},
// the storage engine for 'sqlite'
// default store into ~/cnpmjs.org.sqlite
storage: path.join(process.env.HOME || root, 'cnpmjs.org.sqlite'),
// default store into ~/.cnpmjs.org/data.sqlite
storage: path.join(dataDir, 'data.sqlite'),
logging: !!process.env.SQL_DEBUG,
},
// redis config
// use for koa-limit module as storage
redis: null,
// return total modules and versions, default is true
// it will use `SELECT count(DISTINCT name) FROM module` SQL on Database
enableTotalCount: true,
// enable proxy npm audits request or not
enableNpmAuditsProxy: true,
// package tarball store in local filesystem by default
nfs: require('fs-cnpm')({
dir: path.join(root, '.tmp', 'dist')
nfs: new Nfs({
dir: path.join(dataDir, 'nfs')
}),
// if set true, will 302 redirect to `nfs.url(dist.key)`
downloadRedirectToNFS: false,
// don't check database and just download tgz from nfs
downloadTgzDontCheckModule: false,
// remove original tarball when publishing
unpublishRemoveTarball: true,
// registry url name
registryHost: 'r.cnpmjs.org',
@@ -163,18 +184,13 @@ var config = {
* registry mode config
*/
// enable private mode, only admin can publish, other use just can sync package from source npm
enablePrivate: true,
// enable private mode or not
// private mode: only admins can publish, other users just can sync package from source npm
// public mode: all users can publish
enablePrivate: false,
// 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,
scopes: [ '@cnpm', '@cnpmtest', '@cnpm-test' ],
// some registry already have some private packages in global scope
// but we want to treat them as scoped private packages,
@@ -185,25 +201,19 @@ var config = {
* sync configs
*/
// sync dist config
// sync node.js dist from nodejs.org
noticeSyncDistError: true,
disturl: 'http://nodejs.org/dist',
syncDist: false,
pythonDisturl: 'https://www.python.org/ftp',
syncPythonDist: 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',
officialNpmRegistry: 'https://registry.npmjs.com',
officialNpmReplicate: 'https://replicate.npmjs.com',
cnpmRegistry: 'https://r.cnpmjs.com',
// 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',
sourceNpmRegistry: 'https://registry.npm.taobao.org',
sourceNpmWeb: 'https://npm.taobao.org',
// upstream registry is base on cnpm/cnpmjs.org or not
// if your upstream is official npm registry, please turn it off
@@ -213,10 +223,12 @@ var config = {
syncByInstall: true,
// sync mode select
// none: do not sync any module
// none: do not sync any module, proxy all public modules from sourceNpmRegistry
// exist: only sync exist modules
// all: sync all modules
syncModel: 'none', // 'none', 'all', 'exist'
// sync package.json/dist-tag.json to sync dir
syncBackupFiles: false,
syncConcurrency: 1,
// sync interval, default is 10 minutes
@@ -232,15 +244,121 @@ var config = {
// sync devDependencies or not, default is false
syncDevDependencies: false,
// try to remove all deleted versions from original registry
syncDeletedVersions: true,
// badge subject on http://shields.io/
// changes streaming sync
syncChangesStream: false,
syncDownloadOptions: {
// formatRedirectUrl: function (url, location)
},
handleSyncRegistry: 'http://127.0.0.1:7001',
// default badge subject
badgeSubject: 'cnpm',
// defautl use https://badgen.net/
badgeService: {
url: function(subject, status, options) {
options = options || {};
let url = `https://badgen.net/badge/${utility.encodeURIComponent(subject)}/${utility.encodeURIComponent(status)}`;
if (options.color) {
url += `/${utility.encodeURIComponent(options.color)}`;
}
if (options.icon) {
url += `?icon=${utility.encodeURIComponent(options.icon)}`;
}
return url;
},
},
packagephobiaURL: 'https://packagephobia.now.sh',
packagephobiaSupportPrivatePackage: false,
packagephobiaMinDownloadCount: 1000,
// custom user service, @see https://github.com/cnpm/cnpmjs.org/wiki/Use-Your-Own-User-Authorization
// when you not intend to ingegrate with your company's user system, then use null, it would
// use the default cnpm user system
userService: null,
// always-auth https://docs.npmjs.com/misc/config#always-auth
// Force npm to always require authentication when accessing the registry, even for GET requests.
alwaysAuth: false,
// if you're behind firewall, need to request through http proxy, please set this
// e.g.: `httpProxy: 'http://proxy.mycompany.com:8080'`
httpProxy: null,
// snyk.io root url
snykUrl: 'https://snyk.io',
// https://github.com/cnpm/cnpmjs.org/issues/1149
// if enable this option, must create module_abbreviated and package_readme table in database
enableAbbreviatedMetadata: false,
// global hook function: function* (envelope) {}
// envelope format please see https://github.com/npm/registry/blob/master/docs/hooks/hooks-payload.md#payload
globalHook: null,
opensearch: {
host: '',
},
// redis cache
redisCache: {
enable: false,
connectOptions: null,
},
// custom format full package list
// change `GET /:name` request response body
// use on `controllers/registry/list.js`
formatCustomFullPackageInfoAndVersions: (ctx, packageInfo) => {
return packageInfo;
},
// custom format one package version
// change `GET /:name/:version` request response body
// use on `controllers/registry/show.js`
formatCustomOnePackageVersion: (ctx, packageVersion) => {
return packageVersion;
},
// registry download accelerate map
accelerateHostMap: {},
};
// 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)) {
copy(require(customConfig)).override(config);
if (process.env.NODE_ENV === 'test') {
config.enableAbbreviatedMetadata = true;
config.customRegistryMiddlewares.push((app) => {
return function* (next) {
this.set('x-custom-middleware', 'true');
this.set('x-custom-app-models', typeof app.models.query === 'function' ? 'true' : 'false');
yield next;
};
});
config.customWebMiddlewares.push((app) => {
return function* (next) {
this.set('x-custom-web-middleware', 'true');
this.set('x-custom-web-app-models', typeof app.models.query === 'function' ? 'true' : 'false');
yield next;
};
});
}
if (process.env.NODE_ENV !== 'test') {
var customConfig;
if (process.env.NODE_ENV === 'development') {
customConfig = path.join(root, 'config', 'config.js');
} else {
// 1. try to load `$dataDir/config.json` first, not exists then goto 2.
// 2. load config/config.js, everything in config.js will cover the same key in index.js
customConfig = path.join(dataDir, 'config.json');
if (!fs.existsSync(customConfig)) {
customConfig = path.join(root, 'config', 'config.js');
}
}
if (fs.existsSync(customConfig)) {
copy(require(customConfig)).override(config);
}
}
mkdirp.sync(config.logdir);

View File

@@ -1,19 +1,5 @@
/**!
* 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;
@@ -37,9 +23,10 @@ function* deprecateVersions() {
if (!row) {
// some version not exists
this.status = 400;
const error = '[version_error] Some versions: ' + JSON.stringify(Object.keys(body.versions)) + ' not found';
this.body = {
error: 'version_error',
reason: 'Some versions: ' + JSON.stringify(Object.keys(body.versions)) + ' not found'
error,
reason: error,
};
return;
}
@@ -51,10 +38,10 @@ function* deprecateVersions() {
}
yield updateTasks;
// update last modified
yield* packageService.updateModuleLastModified(name);
yield packageService.updateModuleLastModified(name);
this.status = 201;
this.body = {
ok: true
ok: true,
};
}

View File

@@ -0,0 +1,117 @@
'use strict';
var packageService = require('../../../services/package');
var hook = require('../../../services/hook');
function ok() {
return {
ok: "dist-tags updated"
};
}
// GET /-/package/:pkg/dist-tags -- returns the package's dist-tags
exports.index = function* () {
var name = this.params.name || this.params[0];
var rows = yield packageService.listModuleTags(name);
var tags = {};
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
tags[row.tag] = row.version;
}
this.body = tags;
};
// PUT /-/package/:pkg/dist-tags -- Set package's dist-tags to provided object body (removing missing)
exports.save = function* () {
var name = this.params.name || this.params[0];
yield packageService.removeModuleTags(name);
yield exports.update.call(this);
};
// POST /-/package/:pkg/dist-tags -- Add/modify dist-tags from provided object body (merge)
exports.update = function* () {
var name = this.params.name || this.params[0];
var tags = this.request.body;
for (var tag in tags) {
var version = tags[tag];
yield packageService.addModuleTag(name, tag, version);
// hooks
const envelope = {
event: 'package:dist-tag',
name: name,
tag: tag,
type: 'package',
version: version,
hookOwner: null,
payload: null,
change: null,
};
hook.trigger(envelope);
}
this.status = 201;
this.body = ok();
};
// PUT /-/package/:pkg/dist-tags/:tag -- Set package's dist-tags[tag] to provided string body
// POST /-/package/:pkg/dist-tags/:tag -- Same as PUT /-/package/:pkg/dist-tags/:tag
exports.set = function* () {
var name = this.params.name || this.params[0];
var tag = this.params.tag || this.params[1];
var version = this.request.body;
// make sure version exists
var pkg = yield packageService.getModule(name, version);
if (!pkg) {
this.status = 400;
const error = '[version_error] ' + name + '@' + version + ' not exists';
this.body = {
error,
reason: error,
};
return;
}
yield packageService.addModuleTag(name, tag, version);
this.status = 201;
this.body = ok();
// hooks
const envelope = {
event: 'package:dist-tag',
name: name,
tag: tag,
type: 'package',
version: version,
hookOwner: null,
payload: null,
change: null,
};
hook.trigger(envelope);
};
// DELETE /-/package/:pkg/dist-tags/:tag -- Remove tag from dist-tags
exports.destroy = function* () {
var name = this.params.name || this.params[0];
var tag = this.params.tag || this.params[1];
if (tag === 'latest') {
this.status = 400;
const error = '[dist_tag_error] Can\'t not delete latest tag';
this.body = {
error,
reason: error,
};
return;
}
yield packageService.removeModuleTagsByNames(name, tag);
this.body = ok();
// hooks
const envelope = {
event: 'package:dist-tag:rm',
name: name,
tag: tag,
type: 'package',
hookOwner: null,
payload: null,
change: null,
};
hook.trigger(envelope);
};

View File

@@ -1,72 +1,81 @@
/**!
* 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 is = require('is-type-of');
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 config = require('../../../config');
var _downloads = {};
let globalDownloads = new Map();
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;
var query = this.query || {};
// allow download from specific store bucket
var options = query.bucket ? { bucket: query.bucket } : null;
if (typeof nfs.url === 'function') {
url = nfs.url(common.getCDNKey(name, filename));
if (is.generatorFunction(nfs.url)) {
url = yield nfs.url(common.getCDNKey(name, filename), options);
} else {
url = nfs.url(common.getCDNKey(name, filename), options);
}
}
debug('download %s %s %s %s', name, filename, version, url);
// don't check database and just download tgz from nfs
if (config.downloadTgzDontCheckModule && url) {
this.status = 302;
this.set('Location', url);
const count = (globalDownloads.get(name) || 0) + 1;
globalDownloads.set(name, count);
return;
}
var row = yield packageService.getModule(name, version);
if (!row || !row.package || !row.package.dist) {
if (!url) {
return yield* next;
return yield next;
}
this.status = 302;
this.set('Location', url);
_downloads[name] = (_downloads[name] || 0) + 1;
const count = (globalDownloads.get(name) || 0) + 1;
globalDownloads.set(name, count);
return;
}
const count = (globalDownloads.get(name) || 0) + 1;
globalDownloads.set(name, count);
if (config.downloadRedirectToNFS && url) {
this.status = 302;
this.set('Location', url);
return;
}
var dist = row.package.dist;
if (!dist.key) {
debug('get tarball by 302, url: %s', dist.tarball || url);
// try to use nsf.url() first
url = url || dist.tarball;
debug('get tarball by 302, url: %s', url);
this.status = 302;
this.set('Location', dist.tarball || url);
_downloads[name] = (_downloads[name] || 0) + 1;
this.set('Location', url);
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;
}
@@ -74,22 +83,32 @@ module.exports = function* download(next) {
this.attachment(filename);
this.etag = dist.shasum;
this.body = yield* downloadAsReadStream(dist.key);
this.body = yield downloadAsReadStream(dist.key);
};
var saving = false;
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) {
if (saving) {
return;
}
// save download count
var totals = [];
var allCount = 0;
for (const [ name, count ] of globalDownloads) {
if (name !== '__all__') {
totals.push([name, count]);
}
allCount += count;
}
globalDownloads = new Map();
if (allCount === 0) {
return;
}
saving = true;
totals.push([ '__all__', allCount ]);
debug('save download total: %j', totals);
var date = utility.YYYYMMDD();
@@ -98,12 +117,16 @@ defer.setInterval(function* () {
var name = item[0];
var count = item[1];
try {
yield* downloadTotalService.plusModuleTotal({ name: name, date: date, count: count });
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;
if (err.name !== 'SequelizeUniqueConstraintError') {
err.message += '; name: ' + name + ', count: ' + count + ', date: ' + date;
logger.error(err);
}
// save back to globalDownloads, try again next time
count = (globalDownloads.get(name) || 0) + count;
globalDownloads.set(name, count);
}
}
}, 5000);
saving = false;
}, 5000 + Math.ceil(Math.random() * 1000));

View File

@@ -0,0 +1,67 @@
'use strict';
var DownloadTotal = require('../../../services/download_total');
var DATE_REG = /^\d{4}-\d{2}-\d{2}$/;
module.exports = function* downloadTotal() {
var range = this.params.range || this.params[0] || '';
var name = this.params.name || this.params[1];
range = range.split(':');
if (range.length !== 2
|| !range[0].match(DATE_REG)
|| !range[1].match(DATE_REG)) {
this.status = 400;
const error = '[range_error] range must be YYYY-MM-DD:YYYY-MM-DD style';
this.body = {
error,
reason: error,
};
return;
}
this.body = name
? yield getPackageTotal(name, range[0], range[1])
: yield getTotal(range[0], range[1]);
};
function* getPackageTotal(name, start, end) {
var res = yield DownloadTotal.getModuleTotal(name, start, end);
var downloads = res.map(function (row) {
return {
day: row.date,
downloads: row.count
};
});
downloads.sort(function (a, b) {
return a.day > b.day ? 1 : -1;
});
return {
downloads: downloads,
package: name,
start: start,
end: end
};
}
function* getTotal(start, end) {
var res = yield DownloadTotal.getTotal(start, end);
var downloads = res.map(function (row) {
return {
day: row.date,
downloads: row.count
};
});
downloads.sort(function (a, b) {
return a.day > b.day ? 1 : -1;
});
return {
downloads: downloads,
start: start,
end: end
};
}

View File

@@ -1,41 +1,73 @@
/**!
* 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 utility = require('utility');
var packageService = require('../../../services/package');
var npmService = require('../../../services/npm');
var config = require('../../../config');
var setDownloadURL = require('../../../lib/common').setDownloadURL;
var common = require('../../../lib/common');
var SyncModuleWorker = require('../../sync_module_worker');
var config = require('../../../config');
const cache = require('../../../common/cache');
const logger = require('../../../common/logger');
// https://forum.nginx.org/read.php?2,240120,240120#msg-240120
// should set weak etag avoid nginx remove it
function etag(objs) {
return 'W/"' + utility.md5(JSON.stringify(objs)) + '"';
}
/**
* list all version of a module
* GET /:name
*/
module.exports = function* list() {
var orginalName = this.params.name || this.params[0];
var name = orginalName;
const name = this.params.name || this.params[0];
// sync request will contain this query params
let noCache = this.query.cache === '0';
if (!noCache) {
const ua = this.headers['user-agent'] || '';
// old sync client will request with these user-agent
if (ua.indexOf('npm_service.cnpmjs.org/') !== -1) {
noCache = true;
}
}
const isJSONPRequest = this.query.callback;
let cacheKey;
let needAbbreviatedMeta = false;
let abbreviatedMetaType = 'application/vnd.npm.install-v1+json';
if (config.enableAbbreviatedMetadata && this.accepts([ 'json', abbreviatedMetaType ]) === abbreviatedMetaType) {
needAbbreviatedMeta = true;
if (cache && !isJSONPRequest) {
cacheKey = `list-${name}-v1`;
}
}
if (cacheKey && !noCache) {
const values = yield cache.hmget(cacheKey, 'etag', 'body');
if (values && values[0] && values[1]) {
this.body = values[1];
this.type = 'json';
this.etag = values[0];
this.set('x-hit-cache', cacheKey);
debug('hmget %s success, etag:%j', cacheKey, values[0]);
if (config.registryCacheControlHeader) {
this.set('cache-control', config.registryCacheControlHeader);
}
if (config.registryVaryHeader) {
this.set('vary', config.registryVaryHeader);
}
return;
}
debug('hmget %s missing, %j', cacheKey, values);
}
var rs = yield [
packageService.getModuleLastModified(name),
packageService.listModuleTags(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);
debug('show %s, last modified: %s, tags: %j', name, modifiedTime, tags);
if (modifiedTime) {
// find out the latest modfied time
// because update tags only modfied tag, wont change module gmt_modified
@@ -46,9 +78,6 @@ module.exports = function* list() {
}
}
// use modifiedTime as etag
this.set('ETag', '"' + modifiedTime.getTime() + '"');
// must set status first
this.status = 200;
if (this.fresh) {
@@ -58,6 +87,20 @@ module.exports = function* list() {
}
}
if (needAbbreviatedMeta) {
var rows = yield packageService.listModuleAbbreviatedsByName(name);
if (rows.length > 0) {
yield handleAbbreviatedMetaRequest(this, name, modifiedTime, tags, rows, cacheKey);
return;
}
var fullRows = yield packageService.listModulesByName(name);
if (fullRows.length > 0) {
// no abbreviated meta rows, use the full meta convert to abbreviated meta
yield handleAbbreviatedMetaRequestWithFullMeta(this, name, modifiedTime, tags, fullRows);
return;
}
}
var r = yield [
packageService.listModulesByName(name),
packageService.listStarUserNames(name),
@@ -72,24 +115,27 @@ module.exports = function* list() {
var starUserMap = {};
for (var i = 0; i < starUsers.length; i++) {
starUserMap[starUsers[i]] = true;
var starUser = starUsers[i];
if (starUser[0] !== '"' && starUser[0] !== "'") {
starUserMap[starUser] = true;
}
}
starUsers = starUserMap;
if (rows.length === 0) {
// check if unpublished
var unpublishedInfo = yield* packageService.getUnpublishedModule(name);
var unpublishedInfo = yield packageService.getUnpublishedModule(name);
debug('show unpublished %j', unpublishedInfo);
if (unpublishedInfo) {
this.status = 404;
this.body = {
_id: orginalName,
name: orginalName,
this.jsonp = {
_id: name,
name: name,
time: {
modified: unpublishedInfo.package.time,
unpublished: unpublishedInfo.package,
},
_attachments: {}
_attachments: {},
};
return;
}
@@ -100,34 +146,19 @@ module.exports = function* list() {
if (rows.length === 0) {
if (!this.allowSync) {
this.status = 404;
this.body = {
error: 'not_found',
reason: 'document not found'
const error = '[not_found] document not found';
this.jsonp = {
error,
reason: error,
};
return;
}
// start sync
var logId = yield* SyncModuleWorker.sync(name, 'sync-by-install');
var logId = yield SyncModuleWorker.sync(name, 'sync-by-install');
debug('start sync %s, get log id %s', name, logId);
// 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;
return this.redirect(config.officialNpmRegistry + this.url);
}
var latestMod = null;
@@ -141,15 +172,23 @@ module.exports = function* list() {
// set versions and times
var versions = {};
var allVersionString = '';
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 is string ... ignore it
if (typeof pkg === 'string') {
continue;
}
common.setDownloadURL(pkg, this);
pkg._cnpm_publish_time = row.publish_time;
pkg.publish_time = pkg.publish_time || row.publish_time;
versions[pkg.version] = pkg;
allVersionString += pkg.version + ',';
var t = times[pkg.version] = row.publish_time ? new Date(row.publish_time) : row.gmt_modified;
if ((!distTags.latest && !latestMod) || distTags.latest === pkg.version) {
@@ -191,12 +230,19 @@ module.exports = function* list() {
distTags.latest = pkg.version;
}
if (!readme && config.enableAbbreviatedMetadata) {
var packageReadme = yield packageService.getPackageReadme(name);
if (packageReadme) {
readme = packageReadme.readme;
}
}
var info = {
_id: orginalName,
_id: name,
_rev: rev,
name: orginalName,
name: name,
description: pkg.description,
"dist-tags": distTags,
'dist-tags': distTags,
maintainers: pkg.maintainers,
time: times,
users: starUsers,
@@ -212,6 +258,186 @@ module.exports = function* list() {
info.bugs = pkg.bugs;
info.license = pkg.license;
debug('show module %s: %s, latest: %s', orginalName, rev, latestMod.version);
this.body = info;
if (typeof config.formatCustomFullPackageInfoAndVersions === 'function') {
info = config.formatCustomFullPackageInfoAndVersions(this, info);
}
debug('show module %s: %s, latest: %s', name, rev, latestMod.version);
this.jsonp = info;
// use faster etag
this.etag = etag([
modifiedTime,
distTags,
pkg.maintainers,
allVersionString,
]);
if (config.registryCacheControlHeader) {
this.set('cache-control', config.registryCacheControlHeader);
}
if (config.registryVaryHeader) {
this.set('vary', config.registryVaryHeader);
}
};
function* handleAbbreviatedMetaRequest(ctx, name, modifiedTime, tags, rows, cacheKey) {
debug('show %s got %d rows, %d tags, modifiedTime: %s', name, rows.length, tags.length, modifiedTime);
const isJSONPRequest = ctx.query.callback;
var latestMod = 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 allVersionString = '';
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
var pkg = row.package;
common.setDownloadURL(pkg, ctx);
pkg._publish_on_cnpm = undefined;
pkg.publish_time = pkg.publish_time || row.publish_time;
versions[pkg.version] = pkg;
allVersionString += pkg.version + ',';
if ((!distTags.latest && !latestMod) || distTags.latest === pkg.version) {
latestMod = row;
}
}
if (!latestMod) {
latestMod = rows[0];
}
if (tags.length === 0) {
// some sync error reason, will cause tags missing
// set latest tag at least
distTags.latest = latestMod.package.version;
}
var info = {
name: name,
modified: modifiedTime,
'dist-tags': distTags,
versions: versions,
};
debug('show %j', info);
// use faster etag
const resultEtag = etag([
modifiedTime,
distTags,
allVersionString,
]);
if (isJSONPRequest) {
ctx.jsonp = info;
} else {
ctx.body = JSON.stringify(info);
ctx.type = 'json';
// set cache
if (cacheKey) {
// set cache async, dont block the response
cache.pipeline()
.hmset(cacheKey, 'etag', resultEtag, 'body', ctx.body)
// cache 120s
.expire(cacheKey, 120)
.exec()
.catch(err => {
logger.error(err);
});
}
}
ctx.etag = resultEtag;
if (config.registryCacheControlHeader) {
ctx.set('cache-control', config.registryCacheControlHeader);
}
if (config.registryVaryHeader) {
ctx.set('vary', config.registryVaryHeader);
}
}
function* handleAbbreviatedMetaRequestWithFullMeta(ctx, name, modifiedTime, tags, rows) {
debug('show %s got %d rows, %d tags',
name, rows.length, tags.length);
var latestMod = 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 allVersionString = '';
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
// pkg is string ... ignore it
if (typeof row.package === 'string') {
continue;
}
// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-object
var pkg = {
name: row.package.name,
version: row.package.version,
deprecated: row.package.deprecated,
dependencies: row.package.dependencies,
optionalDependencies: row.package.optionalDependencies,
devDependencies: row.package.devDependencies,
bundleDependencies: row.package.bundleDependencies,
peerDependencies: row.package.peerDependencies,
bin: row.package.bin,
directories: row.package.directories,
dist: row.package.dist,
engines: row.package.engines,
_hasShrinkwrap: row.package._hasShrinkwrap,
publish_time: row.package.publish_time || row.publish_time,
};
common.setDownloadURL(pkg, ctx);
versions[pkg.version] = pkg;
allVersionString += pkg.version + ',';
if ((!distTags.latest && !latestMod) || distTags.latest === pkg.version) {
latestMod = row;
}
}
if (!latestMod) {
latestMod = rows[0];
}
if (tags.length === 0) {
// some sync error reason, will cause tags missing
// set latest tag at least
distTags.latest = latestMod.package.version;
}
var info = {
name: name,
modified: modifiedTime,
'dist-tags': distTags,
versions: versions,
};
debug('show %j', info);
ctx.jsonp = info;
// use faster etag
ctx.etag = etag([
modifiedTime,
distTags,
allVersionString,
]);
if (config.registryCacheControlHeader) {
ctx.set('cache-control', config.registryCacheControlHeader);
}
if (config.registryVaryHeader) {
ctx.set('vary', config.registryVaryHeader);
}
}

View File

@@ -1,19 +1,5 @@
/**!
* 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
@@ -21,7 +7,7 @@ var packageService = require('../../../services/package');
// 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 names = yield packageService.listAllPublicModuleNames();
var result = { _updated: updated };
names.forEach(function (name) {
result[name] = true;

View File

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

View File

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

View File

@@ -1,23 +1,32 @@
/**!
* cnpmjs.org - controllers/registry/package/list_shorts.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');
const packageService = require('../../../services/package');
const config = require('../../../config');
// GET /-/short
// List all packages names only
// List public all packages names only
module.exports = function* () {
this.body = yield* packageService.listAllPublicModuleNames();
if (this.query.private_only) {
const tasks = [];
for (let i = 0; i < config.scopes.length; i++) {
const scope = config.scopes[i];
tasks.push(packageService.listPrivateModulesByScope(scope));
}
if (config.privatePackages && config.privatePackages.length > 0) {
tasks.push(packageService.listModules(config.privatePackages));
}
const results = yield tasks;
const names = [];
for (const rows of results) {
for (const row of rows) {
names.push(row.name);
}
}
this.body = names;
return;
}
this.body = yield packageService.listAllPublicModuleNames();
};

View File

@@ -1,33 +1,21 @@
/**!
* 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;
var TWA_DAYS_MS = 3600000 * 24 * 2;
// 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* () {
module.exports = function* listSince() {
var query = this.query;
if (query.stale !== 'update_after') {
this.status = 400;
const error = '[query_parse_error] Invalid value for `stale`.';
this.body = {
error: 'query_parse_error',
reason: 'Invalid value for `stale`.'
error,
reason: error,
};
return;
}
@@ -35,21 +23,22 @@ module.exports = function* () {
var startkey = Number(query.startkey);
if (!startkey) {
this.status = 400;
const error = '[query_parse_error] Invalid value for `startkey`.';
this.body = {
error: 'query_parse_error',
reason: 'Invalid value for `startkey`.'
error,
reason: error,
};
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);
startkey = updated - TWA_DAYS_MS;
console.warn('[%s] list modules since time out of range: query: %j, ip: %s, limit to %s',
Date(), query, this.ip, startkey);
}
var names = yield* packageService.listPublicModuleNamesSince(startkey);
var names = yield packageService.listPublicModuleNamesSince(startkey);
var result = { _updated: updated };
names.forEach(function (name) {
result[name] = true;

View File

@@ -0,0 +1,37 @@
'use strict';
const moment = require('moment');
const packageService = require('../../../services/package');
// GET /-/allversions?date={2020-02-20}
// List all packages versions sync at date(gmt_modified)
module.exports = function* () {
const query = this.query;
const date = moment(query.date, 'YYYY-MM-DD');
if (!date.isValid()) {
this.status = 400;
const error = '[query_parse_error] Invalid value for `date`, should be `YYYY-MM-DD` format.';
this.body = {
error,
reason: error,
};
return;
}
const today = date.format('YYYY-MM-DD');
const rows = yield packageService.findAllModuleAbbreviateds({
gmt_modified: {
$gte: `${today} 00:00:00`,
$lte: `${today} 23:59:59`,
},
});
this.body = rows.map(row => {
return {
name: row.name,
version: row.version,
publish_time: new Date(row.publish_time),
gmt_modified: row.gmt_modified,
};
});
};

View File

@@ -1,25 +1,12 @@
/**!
* 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');
var config = require('../../../config');
// DELETE /:name/-rev/:rev
// https://github.com/npm/npm-registry-client/blob/master/lib/unpublish.js#L25
@@ -28,11 +15,11 @@ module.exports = function* remove(next) {
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);
var mods = yield packageService.listModulesByName(name);
debug('removeAll module %s: %d', name, mods.length);
var mod = mods[0];
if (!mod) {
return yield* next;
return yield next;
}
yield [
@@ -41,27 +28,29 @@ module.exports = function* remove(next) {
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;
if (config.unpublishRemoveTarball) {
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);
}
key && keys.push(key);
}
try {
yield keys.map(function (key) {
return nfs.remove(key);
});
} catch (err) {
logger.error(err);
try {
yield keys.map(function (key) {
return nfs.remove(key);
});
} catch (err) {
logger.error(err);
}
}
// remove the maintainers
yield* packageService.removeAllMaintainers(name);
yield packageService.removeAllMaintainers(name);
this.body = { ok: true };
};

View File

@@ -1,24 +1,11 @@
/**!
* 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;
var config = require('../../../config');
// DELETE /:name/download/:filename/-rev/:rev
// https://github.com/npm/npm-registry-client/blob/master/lib/unpublish.js#L97
@@ -33,13 +20,13 @@ module.exports = function* removeOneVersion(next) {
version = version.substring(0, version.lastIndexOf('.tgz'));
}
if (!version) {
return yield* next;
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;
return yield next;
}
var rs = yield [
@@ -49,26 +36,30 @@ module.exports = function* removeOneVersion(next) {
var revertTo = rs[0];
var mod = rs[1]; // module need to delete
if (!mod || mod.name !== name) {
return yield* next;
return yield next;
}
var key = mod.package && mod.package.dist && mod.package.dist.key;
if (!key) {
key = getCDNKey(mod.name, filename);
if (config.unpublishRemoveTarball) {
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);
}
}
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]);
yield packageService.removeModulesByNameAndVersions(name, [version]);
debug('removed %s@%s', name, version);
this.body = { ok: true };
};

View File

@@ -1,19 +1,5 @@
/**!
* 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');
@@ -21,6 +7,7 @@ var packageService = require('../../../services/package');
var common = require('../../../lib/common');
var nfs = require('../../../common/nfs');
var config = require('../../../config');
var hook = require('../../../services/hook');
// old flows:
// 1. add()
@@ -44,21 +31,23 @@ module.exports = function* save(next) {
var version = Object.keys(pkg.versions || {})[0];
if (!version) {
this.status = 400;
const error = '[version_error] package.versions is empty';
this.body = {
error: 'version_error',
reason: 'package.versions is empty'
error,
reason: error,
};
return;
}
// check maintainers
var result = yield* packageService.authMaintainer(name, username);
var result = yield packageService.authMaintainer(name, username);
if (!result.isMaintainer) {
this.status = 403;
const error = '[forbidden] ' + username + ' not authorized to modify ' + name +
', please contact maintainers: ' + result.maintainers.join(', ');
this.body = {
error: 'forbidden user',
reason: username + ' not authorized to modify ' + name +
', please contact maintainers: ' + result.maintainers.join(', ')
error,
reason: error,
};
return;
}
@@ -73,13 +62,14 @@ module.exports = function* save(next) {
}
}
if (hasDeprecated) {
return yield* deprecateVersions.call(this, next);
return yield deprecateVersions.call(this, next);
}
this.status = 400;
const error = '[attachment_error] package._attachments is empty';
this.body = {
error: 'attachment_error',
reason: 'package._attachments is empty'
error,
reason: error,
};
return;
}
@@ -88,14 +78,25 @@ module.exports = function* save(next) {
var versionPackage = pkg.versions[version];
var maintainers = versionPackage.maintainers;
// should never happened in normal request
var authorizeType = common.getAuthorizeType(this);
if (!maintainers) {
this.status = 400;
this.body = {
error: 'maintainers error',
reason: 'request body need maintainers'
};
return;
if (authorizeType === common.AuthorizeType.BEARER) {
// With the token mode, pub lib with no maintainers
// make the maintainer to be puber
maintainers = [{
name: this.user.name,
email: this.user.email,
}];
} else {
// should never happened in normal request
this.status = 400;
const error = '[maintainers_error] request body need maintainers';
this.body = {
error,
reason: error,
};
return;
}
}
// notice that admins can not publish to all modules
@@ -103,16 +104,19 @@ module.exports = function* save(next) {
// 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;
if (authorizeType !== common.AuthorizeType.BEARER) {
var m = maintainers.filter(function (maintainer) {
return maintainer.name === username;
});
if (m.length === 0) {
this.status = 403;
const error = '[maintainers_error] ' + username + ' does not in maintainer list';
this.body = {
error,
reason: error,
};
return;
}
}
// TODO: add this info into some table
@@ -125,9 +129,10 @@ module.exports = function* save(next) {
if (tags.length === 0) {
this.status = 400;
const error = '[invalid] dist-tags should not be empty';
this.body = {
error: 'invalid',
reason: 'dist-tags should not be empty'
error,
reason: error,
};
return;
}
@@ -135,34 +140,36 @@ module.exports = function* save(next) {
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 exists = yield packageService.getModule(name, version);
var shasum;
if (exists) {
this.status = 403;
const error = '[forbidden] cannot modify pre-existing version: ' + version;
this.body = {
error: 'forbidden',
reason: 'cannot modify pre-existing version: ' + version
error,
reason: error,
};
return;
}
// upload attachment
var tarballBuffer;
tarballBuffer = new Buffer(attachment.data, 'base64');
tarballBuffer = Buffer.from(attachment.data, 'base64');
if (tarballBuffer.length !== attachment.length) {
this.status = 403;
const error = '[size_wrong] Attachment size ' + attachment.length
+ ' not match download size ' + tarballBuffer.length;
this.body = {
error: 'size_wrong',
reason: 'Attachment size ' + attachment.length
+ ' not match download size ' + tarballBuffer.length,
error,
reason: error,
};
return;
}
if (!distTags.latest) {
// need to check if latest tag exists or not
var latest = yield* packageService.getModuleByTag(name, 'latest');
var latest = yield packageService.getModuleByTag(name, 'latest');
if (!latest) {
// auto add latest
tags.push(['latest', tags[0][1]]);
@@ -202,9 +209,9 @@ module.exports = function* save(next) {
};
mod.package.dist = dist;
yield* addDepsRelations(mod.package);
yield addDepsRelations(mod.package);
var addResult = yield* packageService.saveModule(mod);
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);
@@ -219,13 +226,25 @@ module.exports = function* save(next) {
var maintainerNames = maintainers.map(function (item) {
return item.name;
});
yield* packageService.addPrivateModuleMaintainers(name, maintainerNames);
yield packageService.addPrivateModuleMaintainers(name, maintainerNames);
this.status = 201;
this.body = {
ok: true,
rev: String(addResult.id)
};
// hooks
const envelope = {
event: 'package:publish',
name: mod.name,
type: 'package',
version: mod.version,
hookOwner: null,
payload: null,
change: null,
};
hook.trigger(envelope);
};
function* addDepsRelations(pkg) {

View File

@@ -1,24 +1,7 @@
/**!
* 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');
@@ -33,52 +16,37 @@ var config = require('../../../config');
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);
}
var mod = yield packageService.showPackage(name, tag, this);
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;
if (typeof config.formatCustomOnePackageVersion === 'function') {
mod.package = config.formatCustomOnePackageVersion(this, mod.package);
}
this.jsonp = mod.package;
if (config.registryCacheControlHeader) {
this.set('cache-control', config.registryCacheControlHeader);
}
if (config.registryVaryHeader) {
this.set('vary', config.registryVaryHeader);
}
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
const error = '[not_exists] version not found: ' + tag;
this.jsonp = {
error,
reason: error,
};
return;
}
// start sync
var logId = yield* SyncModuleWorker.sync(name, 'sync-by-install');
var logId = yield SyncModuleWorker.sync(name, 'sync-by-install');
debug('start sync %s, get log id %s', name, logId);
// 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;
this.redirect(config.officialNpmRegistry + this.url);
};

View File

@@ -1,19 +1,5 @@
/**!
* 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');
@@ -30,40 +16,41 @@ module.exports = function* tag() {
if (!version) {
this.status = 400;
const error = '[version_missed] version not found';
this.body = {
error: 'version_missed',
reason: 'version not found'
error,
reason: error,
};
return;
}
if (!semver.valid(version)) {
this.status = 403;
var reason = util.format('setting tag %s to invalid version: %s: %s/%s',
const error = util.format('[forbidden] setting tag %s to invalid version: %s: %s/%s',
tag, version, name, tag);
this.body = {
error: 'forbidden',
reason: reason
error,
reason: error,
};
return;
}
var mod = yield* packageService.getModule(name, version);
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',
const error = util.format('[forbidden] setting tag %s to unknown version: %s: %s/%s',
tag, version, name, tag);
this.body = {
error: 'forbidden',
reason: reason
error,
reason: error,
};
return;
}
var row = yield* packageService.addModuleTag(name, tag, version);
var row = yield packageService.addModuleTag(name, tag, version);
this.status = 201;
this.body = {
ok: true,
modified: row.gmt_modified
modified: row.gmt_modified,
};
};

View File

@@ -1,23 +1,10 @@
/**!
* 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');
var hook = require('../../../services/hook');
// PUT /:name/-rev/:rev
//
@@ -29,11 +16,11 @@ module.exports = function* update(next) {
var body = this.request.body;
if (body.versions) {
yield* updateVersions.call(this, next);
yield updateVersions.call(this, next);
} else if (body.maintainers) {
yield* updateMaintainers.call(this, next);
yield updateMaintainers.call(this, next);
} else {
yield* next;
yield next;
}
};
@@ -45,11 +32,11 @@ function* updateVersions(next) {
var versions = this.request.body.versions;
// step1: list all the versions
var mods = yield* packageService.listModulesByName(name);
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;
return yield next;
}
// step3: calculate which versions need to remove and
@@ -78,7 +65,7 @@ function* updateVersions(next) {
// 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 tags = yield packageService.listModuleTags(name);
var removeTags = [];
var latestRemoved = false;
@@ -95,17 +82,17 @@ function* updateVersions(next) {
debug('remove tags: %j', removeTags);
if (removeTags.length) {
// step 5: remove all the tags
yield* packageService.removeModuleTagsByIds(removeTags);
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]);
yield packageService.addModuleTag(name, 'latest', remainVersions[0]);
}
}
// step 7: update last modified, make sure etag change
yield* packageService.updateModuleLastModified(name);
yield packageService.updateModuleLastModified(name);
this.status = 201;
this.body = { ok: true };
@@ -122,16 +109,17 @@ function* updateMaintainers() {
if (usernames.length === 0) {
this.status = 403;
const error = '[invalid_operation] Can not remove all maintainers';
this.body = {
error: 'invalid operation',
reason: 'Can not remove all maintainers'
error,
reason: error,
};
return;
}
if (config.customUserService) {
// ensure new authors are vaild
var maintainers = yield* packageService.listMaintainerNamesOnly(name);
var maintainers = yield packageService.listMaintainerNamesOnly(name);
var map = {};
var newNames = [];
for (var i = 0; i < maintainers.length; i++) {
@@ -144,7 +132,7 @@ function* updateMaintainers() {
}
}
if (newNames.length > 0) {
var users = yield* userService.list(newNames);
var users = yield userService.list(newNames);
var map = {};
for (var i = 0; i < users.length; i++) {
var user = users[i];
@@ -159,17 +147,43 @@ function* updateMaintainers() {
}
if (invailds.length > 0) {
this.status = 403;
const error = '[invalid] User: `' + invailds.join(', ') + '` not exists';
this.body = {
error: 'invalid user name',
reason: 'User: `' + invailds.join(', ') + '` not exists'
error,
reason: error,
};
return;
}
}
}
var r = yield* packageService.updatePrivateModuleMaintainers(name, usernames);
var r = yield packageService.updatePrivateModuleMaintainers(name, usernames);
debug('result: %j', r);
if (r.add && r.add.length) {
const envelope = {
event: 'package:owner',
name: name,
type: 'package',
version: null,
hookOwner: null,
payload: null,
change: null,
};
hook.trigger(envelope);
}
if (r.remove && r.remove.length) {
const envelope = {
event: 'package:owner-rm',
name: name,
type: 'package',
version: null,
hookOwner: null,
payload: null,
change: null,
};
hook.trigger(envelope);
}
this.status = 201;
this.body = {

View File

@@ -0,0 +1,53 @@
'use strict';
var ipRegex = require('ip-regex');
var tokenService = require('../../../services/token');
var userService = require('../../../services/user');
var ipv4 = ipRegex.v4({ exact: true });
module.exports = function* createToken() {
var readonly = this.request.body.readonly;
if (typeof readonly !== 'undefined' && typeof readonly !== 'boolean') {
this.status = 400;
var error = '[bad_request] readonly ' + readonly + ' is not boolean';
this.body = {
error,
reason: error,
};
return;
}
var cidrWhitelist = this.request.body.cidr_whitelist;
if (typeof cidrWhitelist !== 'undefined') {
var isValidateWhiteList = Array.isArray(cidrWhitelist) && cidrWhitelist.every(function (cidr) {
return ipv4.test(cidr);
});
if (!isValidateWhiteList) {
this.status = 400;
var error = '[bad_request] cide white list ' + JSON.stringify(cidrWhitelist) + ' is not validate ip array';
this.body = {
error,
reason: error,
};
return;
}
}
var password = this.request.body.password;
var user = yield userService.auth(this.user.name, password);
if (!user) {
this.status = 401;
var error = '[unauthorized] incorrect or missing password.';
this.body = {
error,
reason: error,
};
return;
}
var token = yield tokenService.createToken(this.user.name, {
readonly: !!readonly,
cidrWhitelist: cidrWhitelist || [],
});
this.status = 201;
this.body = token;
};

View File

@@ -0,0 +1,8 @@
'use strict';
var tokenService = require('../../../services/token');
module.exports = function* deleteToken() {
yield tokenService.deleteToken(this.user.name, this.params.UUID);
this.status = 204;
};

View File

@@ -0,0 +1,60 @@
'use strict';
var tokenService = require('../../../services/token');
var DEFAULT_PER_PAGE = 10;
var MIN_PER_PAGE = 1;
var MAX_PER_PAGE = 9999;
module.exports = function* createToken() {
var perPage = typeof this.query.perPage === 'undefined' ? DEFAULT_PER_PAGE : parseInt(this.query.perPage);
if (Number.isNaN(perPage)) {
this.status = 400;
var error = 'perPage ' + this.query.perPage + ' is not a number';
this.body = {
error,
reason: error,
};
return;
}
if (perPage < MIN_PER_PAGE || perPage > MAX_PER_PAGE) {
this.status = 400;
var error = 'perPage ' + this.query.perPage + ' is out of boundary';
this.body = {
error,
reason: error,
};
return;
}
var page = typeof this.query.page === 'undefined' ? 0 : parseInt(this.query.page);
if (Number.isNaN(page)) {
this.status = 400;
var error = 'page ' + this.query.page + ' is not a number';
this.body = {
error,
reason: error,
};
return;
}
if (page < 0) {
this.status = 400;
var error = 'page ' + this.query.page + ' is invalidate';
this.body = {
error,
reason: error,
};
return;
}
var tokens = yield tokenService.listToken(this.user.name, {
page: page,
perPage: perPage,
});
this.status = 200;
this.body = {
objects: tokens,
urls: {},
};
};

View File

@@ -1,23 +1,9 @@
/**!
* 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');
var tokenService = require('../../../services/token');
// npm 1.4.4
// add new user first
@@ -55,6 +41,54 @@ var config = require('../../../config');
module.exports = function* addUser() {
var name = this.params.name;
var body = this.request.body || {};
if (!body.password || !body.name) {
this.status = 422;
const error = '[param_error] params missing, name, email or password missing';
this.body = {
error,
reason: error,
};
return;
}
var loginedUser;
try {
loginedUser = yield userService.authAndSave(body.name, body.password);
} catch (err) {
this.status = err.status || 500;
this.body = {
error: err.message,
reason: err.message,
};
return;
}
if (loginedUser) {
var token = yield tokenService.createToken(body.name, {
readonly: !!body.readonly,
cidrWhitelist: body.cidr_whitelist || [],
});
this.status = 201;
this.body = {
token: token.token,
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;
const error = '[unauthorized] Login fail, please check your login name and password';
this.body = {
error,
reason: error,
};
return;
}
var user = {
name: body.name,
// salt: body.salt,
@@ -66,61 +100,39 @@ module.exports = function* addUser() {
ensurePasswordSalt(user, body);
if (!body.password || !user.name || !user.salt || !user.password_sha || !user.email) {
if (!user.salt || !user.password_sha || !user.email) {
this.status = 422;
const error = '[param_error] params missing, name, email or password missing';
this.body = {
error: 'paramError',
reason: 'params missing, name, email or password missing.'
error,
reason: error,
};
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);
var existUser = yield userService.get(name);
if (existUser) {
this.status = 409;
const error = '[conflict] User ' + name + ' already exists';
this.body = {
error: 'conflict',
reason: 'User ' + name + ' already exists.'
error,
reason: error,
};
return;
}
// add new user
var result = yield* userService.add(user);
var result = yield userService.add(user);
this.etag = '"' + result.rev + '"';
var token = yield tokenService.createToken(body.name, {
readonly: !!body.readonly,
cidrWhitelist: body.cidr_whitelist || [],
});
this.status = 201;
this.body = {
token: token.token,
ok: true,
id: 'org.couchdb.user:' + name,
rev: result.rev

View File

@@ -0,0 +1,7 @@
'use strict';
// https://docs.npmjs.com/cli/ping
module.exports = function* () {
this.status = 200;
this.body = {};
};

View File

@@ -1,28 +1,13 @@
/**!
* 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);
var user = yield userService.getAndSave(name);
if (!user) {
return yield* next;
return yield next;
}
var data = user.json;

View File

@@ -1,20 +1,5 @@
/**!
* 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');
@@ -40,16 +25,17 @@ module.exports = function* updateUser(next) {
var name = this.params.name;
var rev = this.params.rev;
if (!name || !rev) {
return yield* next;
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;
const error = '[unauthorized] Name is incorrect';
this.body = {
error: 'unauthorized',
reason: 'Name is incorrect.'
error,
reason: error,
};
return;
}
@@ -71,19 +57,21 @@ module.exports = function* updateUser(next) {
if (!body.password || !user.name || !user.salt || !user.password_sha || !user.email) {
this.status = 422;
const error = '[param_error] params missing, name, email or password missing';
this.body = {
error: 'paramError',
reason: 'params missing, name, email or password missing.'
error,
reason: error,
};
return;
}
var result = yield* userService.update(user);
var result = yield userService.update(user);
if (!result) {
this.status = 409;
const error = '[conflict] Document update conflict';
this.body = {
error: 'conflict',
reason: 'Document update conflict.'
error,
reason: error,
};
return;
}

View File

@@ -0,0 +1,9 @@
'use strict';
// https://docs.npmjs.com/cli/whoami
module.exports = function* () {
this.status = 200;
this.body = {
username: this.user.name,
};
};

View File

@@ -21,9 +21,10 @@ exports.list = function* () {
var users = this.params.user.split('|');
if (users.length > 20) {
this.status = 400;
const error = '[bad_request] reach max user names limit, must <= 20 user names';
this.body = {
error: 'bad_request',
reason: 'reach max user names limit, must <= 20 user names'
error,
reason: error,
};
return;
}

View File

@@ -1,19 +1,5 @@
/**!
* cnpmjs.org - controllers/sync.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:controllers:sync');
var Log = require('../services/module_log');
var SyncModuleWorker = require('./sync_module_worker');
@@ -21,26 +7,42 @@ var config = require('../config');
exports.sync = function* () {
var username = this.user.name || 'anonymous';
var name = this.params.name;
var name = this.params.name || this.params[0];
var type = 'package';
if (name.indexOf(':') > 0) {
// user:fengmk2
// package:pedding
var splits = name.split(':');
type = splits[0];
name = splits[1];
}
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) {
var syncUpstreamFirst = this.query.sync_upstream === 'true';
var syncFromBackupFile = this.query.sync_from_backup === 'true';
if (!config.sourceNpmRegistryIsCNpm) {
syncUpstreamFirst = false;
}
debug('sync %s with query: %j, syncUpstreamFirst: %s', name, this.query, syncUpstreamFirst);
if (type === 'package' && publish && !this.user.isAdmin) {
this.status = 403;
const error = '[no_perms] Only admin can publish';
this.body = {
error: 'no_perms',
reason: 'Only admin can publish'
error,
reason: error,
};
return;
}
var options = {
type: type,
publish: publish,
noDep: noDep,
syncUpstreamFirst: config.sourceNpmRegistryIsCNpm,
syncUpstreamFirst: syncUpstreamFirst,
syncFromBackupFile: syncFromBackupFile,
};
var logId = yield* SyncModuleWorker.sync(name, username, options);
var logId = yield SyncModuleWorker.sync(name, username, options);
debug('sync %s got log id %j', name, logId);
this.status = 201;
@@ -51,16 +53,27 @@ exports.sync = function* () {
};
exports.getSyncLog = function* (next) {
var logId = this.params.id;
var logId = Number(this.params.id || this.params[1]);
var offset = Number(this.query.offset) || 0;
var row = yield* Log.get(logId);
if (!logId) { // NaN
this.status = 404;
return;
}
var row = yield Log.get(logId);
if (!row) {
return yield* next;
return yield next;
}
var log = row.log.trim();
var syncDone = row.log.indexOf('[done] Sync') >= 0;
if (offset > 0) {
log = log.split('\n').slice(offset).join('\n');
if (!log && syncDone) {
// append the last 1k string
// the cnpm client sync need the `[done] Sync {name}` string to detect when sync task finished
log = '... ignore long logs ...\n' + row.log.substring(row.log.length - 1024);
}
}
this.body = {ok: true, log: log};
this.body = { ok: true, syncDone: syncDone, log: log };
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,31 +1,39 @@
/**!
* cnpmjs.org - controllers/total.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
'use strict';
/**
* Module dependencies.
*/
const Total = require('../services/total');
const version = require('../package.json').version;
const config = require('../config');
const getDownloadTotal = require('./utils').getDownloadTotal;
const cacheClient = require('../common/cache');
const logger = require('../common/logger');
var Total = require('../services/total');
var version = require('../package.json').version;
var config = require('../config');
var getDownloadTotal = require('./utils').getDownloadTotal;
var startTime = '' + Date.now();
const startTime = '' + Date.now();
let cache = null;
module.exports = function* showTotal() {
var r = yield [Total.get(), getDownloadTotal()];
var total = r[0];
var download = r[1];
if (cache && Date.now() - cache.cache_time < 120000) {
// cache 120 seconds
this.body = cache;
return;
}
const cacheKey = 'registry_total';
if (cacheClient) {
const result = yield cacheClient.get(cacheKey);
if (result) {
this.body = JSON.parse(result);
return;
}
}
if (cache) {
// set cache_time fisrt, avoid query in next time
cache.cache_time = Date.now();
}
const r = yield [ Total.get(), getDownloadTotal() ];
const total = r[0];
const download = r[1];
total.download = download;
total.db_name = 'registry';
@@ -35,5 +43,18 @@ module.exports = function* showTotal() {
total.donate = 'https://www.gittip.com/fengmk2';
total.sync_model = config.syncModel;
cache = total;
cache.cache_time = Date.now();
this.body = total;
if (cacheClient) {
cacheClient.pipeline()
.set(cacheKey, JSON.stringify(total))
// cache 12h
.expire(cacheKey, 3600 * 12)
.exec()
.catch(err => {
logger.error(err);
});
}
};

View File

@@ -1,25 +1,12 @@
/**!
* 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 rimraf = require('rimraf');
var downloadTotalService = require('../services/download_total');
var nfs = require('../common/nfs');
var config = require('../config');
@@ -27,21 +14,30 @@ var config = require('../config');
var DOWNLOAD_TIMEOUT = ms('10m');
exports.downloadAsReadStream = function* (key) {
var options = { timeout: DOWNLOAD_TIMEOUT };
if (nfs.createDownloadStream) {
return yield nfs.createDownloadStream(key, options);
}
var tmpPath = path.join(config.uploadDir,
utility.randomString() + key.replace(/\//g, '-'));
var tarball;
function cleanup() {
debug('cleanup %s', tmpPath);
fs.unlink(tmpPath, utility.noop);
rimraf(tmpPath, utility.noop);
if (tarball) {
tarball.destroy();
}
}
debug('downloadAsReadStream() %s to %s', key, tmpPath);
try {
yield nfs.download(key, tmpPath, {timeout: DOWNLOAD_TIMEOUT});
yield nfs.download(key, tmpPath, options);
} catch (err) {
debug('downloadAsReadStream() %s to %s error: %s', key, tmpPath, err.stack);
cleanup();
throw err;
}
var tarball = fs.createReadStream(tmpPath);
tarball = fs.createReadStream(tmpPath);
tarball.once('error', cleanup);
tarball.once('end', cleanup);
return tarball;
@@ -51,11 +47,11 @@ 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 lastweekStart = end.clone().subtract(1, 'weeks').startOf('isoweek');
var lastweekEnd = lastweekStart.clone().endOf('isoweek').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');
var thisweekStart = end.clone().startOf('isoweek').format('YYYY-MM-DD');
start = start.format('YYYY-MM-DD');
end = end.format('YYYY-MM-DD');
lastweekStart = lastweekStart.format('YYYY-MM-DD');
@@ -65,7 +61,7 @@ exports.getDownloadTotal = function* (name) {
args.unshift(name);
}
var rows = yield* downloadTotalService[method].apply(downloadTotalService, args);
var rows = yield downloadTotalService[method].apply(downloadTotalService, args);
var download = {
today: 0,
thisweek: 0,

View File

@@ -1,29 +1,22 @@
/**!
* 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');
var DownloadTotal = require('../../services/download_total');
exports.version = function* () {
var color = 'lightgrey';
var version = 'invalid';
var color = 'grey';
var name = this.params[0];
var latestTag = yield* packageService.getModuleByTag(name, 'latest');
if (latestTag) {
version = latestTag.version;
var tag = this.query.tag || 'latest';
var version = this.query.version;
let info;
if (version) {
info = yield packageService.getModule(name, version);
} else {
info = yield packageService.getModuleByTag(name, tag);
}
if (info) {
version = info.version;
if (/^0\.0\./.test(version)) {
// <0.1.0 & >=0.0.0
color = 'red';
@@ -36,14 +29,23 @@ exports.version = function* () {
}
}
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';
var subject = config.badgeSubject;
if (this.query.subject) {
subject = this.query.subject;
}
if (!version) {
version = 'invalid';
}
var style = this.query.style || 'flat-square';
var url = config.badgeService.url(subject, version, { color, style });
this.redirect(url);
};
exports.downloads = function* () {
// https://dn-img-shields-io.qbox.me/badge/downloads-100k/month-brightgreen.svg?style=flat-square
var name = this.params[0];
var count = yield DownloadTotal.getTotalByName(name);
var style = this.query.style;
var url = config.badgeService.url('downloads', count, { style });
this.redirect(url);
};

View File

@@ -1,100 +0,0 @@
/**!
* 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,20 +1,5 @@
/**!
* 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');

View File

@@ -1,28 +1,26 @@
/**!
* 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');
var config = require('../../../config');
module.exports = function* search() {
var params = this.params;
var word = params.word || params[0];
var limit = Number(this.query.limit) || 100;
if (limit > 10000) {
limit = 10000;
}
if (config.disableSearch) {
return this.redirect(`/package/${encodeURIComponent(word)}`);
}
debug('search %j', word);
var result = yield* packageService.search(word);
var result = yield packageService.search(word, {
limit: limit
});
var match = null;
for (var i = 0; i < result.searchMatchs.length; i++) {
@@ -35,13 +33,12 @@ module.exports = function* search() {
// return a json result
if (this.query && this.query.type === 'json') {
this.body = {
this.jsonp = {
keyword: word,
match: match,
packages: result.searchMatchs,
keywords: result.keywordMatchs,
};
this.type = 'application/json; charset=utf-8';
return;
}
yield this.render('search', {

View File

@@ -1,20 +1,5 @@
/**!
* 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() {
@@ -26,7 +11,7 @@ module.exports = function* searchRange() {
startKey = startKey.substring(0, startKey.length - 1);
}
var limit = Number(this.query.limit) || 20;
var result = yield* packageService.search(startKey, {limit: limit});
var result = yield packageService.search(startKey, {limit: limit});
var packages = result.searchMatchs.concat(result.keywordMatchs);

View File

@@ -1,32 +1,18 @@
/**!
* 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 renderMarkdown = require('../../../common/markdown').render;
var packageService = require('../../../services/package');
var downloadTotalService = require('../../../services/download_total');
module.exports = function* show(next) {
var params = this.params;
@@ -49,9 +35,17 @@ module.exports = function* show(next) {
}
var pkg = yield packageService[getPackageMethod].apply(packageService, getPackageArgs);
if ((!pkg || !pkg.package) && tag) {
// + if we can't find it by tag, it may be a non-semver version, check it then
// + if we can't find it by version, it may be a tag, check it then
getPackageMethod = version ? 'getModuleByTag' : 'getModule';
pkg = yield packageService[getPackageMethod](name, tag);
}
// if it's still not found
if (!pkg || !pkg.package) {
// check if unpublished
var unpublishedInfo = yield* packageService.getUnpublishedModule(name);
var unpublishedInfo = yield packageService.getUnpublishedModule(name);
debug('show unpublished %j', unpublishedInfo);
if (unpublishedInfo) {
var data = {
@@ -68,29 +62,67 @@ module.exports = function* show(next) {
}
}
yield this.render('package_unpublished', {
package: data
package: data,
title: 'Package - ' + name
});
return;
}
return yield* next;
return yield next;
}
var r = yield [
utils.getDownloadTotal(name),
packageService.listDependents(name),
packageService.listStarUserNames(name),
packageService.listMaintainers(name)
packageService.listMaintainers(name),
packageService.listModulesByName(name),
packageService.listModuleTags(name),
downloadTotalService.getTotalByName(name),
];
var download = r[0];
var dependents = r[1];
var users = r[2];
var maintainers = r[3];
var rows = r[4];
var tags = r[5];
var downloadTotal = r[6];
const versionsMap = {};
const versions = [];
for (const row of rows) {
var versionPkg = row.package;
// pkg is string ... ignore it
if (typeof versionPkg === 'string') {
continue;
}
versionPkg.fromNow = moment(versionPkg.publish_time || row.publish_time).fromNow();
versions.push(versionPkg);
versionsMap[versionPkg.version] = versionPkg;
}
for (const row of tags) {
row.fromNow = versionsMap[row.version] && versionsMap[row.version].fromNow;
}
pkg.package.fromNow = moment(pkg.publish_time).fromNow();
pkg = pkg.package;
pkg.users = users;
pkg.readme = marked(pkg.readme || '');
pkg.versions = versions;
pkg.tags = tags;
if (!pkg.readme && config.enableAbbreviatedMetadata) {
var packageReadme = yield packageService.getPackageReadme(name);
if (packageReadme) {
pkg.readme = packageReadme.readme;
}
}
if (pkg.readme && typeof pkg.readme !== 'string') {
pkg.readme = 'readme is not string: ' + JSON.stringify(pkg.readme);
} else {
pkg.readme = renderMarkdown(pkg.readme || '');
}
if (!pkg.readme) {
pkg.readme = pkg.description || '';
}
@@ -115,27 +147,13 @@ module.exports = function* show(next) {
}
}
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 === 'undefined') {
pkg.repository = null;
}
if (pkg.repository && pkg.repository.url) {
pkg.repository.weburl = giturl.parse(pkg.repository.url) || pkg.repository.url;
if (!pkg.repository.weburl) {
pkg.repository.weburl = /^https?:\/\//.test(pkg.repository.url) ? pkg.repository.url : (giturl.parse(pkg.repository.url) || pkg.repository.url);
}
}
if (!pkg.bugs) {
pkg.bugs = {};
@@ -159,6 +177,7 @@ module.exports = function* show(next) {
}
pkg.registryUrl = '//' + config.registryHost + '/' + pkg.name;
pkg.registryPackageUrl = '//' + config.registryHost + '/' + pkg.name + '/' + pkg.version;
// pkg.engines = {
// "python": ">= 0.11.9",
@@ -167,37 +186,58 @@ module.exports = function* show(next) {
// "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';
}
// "engines": "0.10.24",
// invalid engines
if (pkg.engines && typeof pkg.engines !== 'object') {
pkg.engines = {};
}
for (var k in pkg.engines) {
var engine = String(pkg.engines[k] || '').trim();
var color = 'blue';
if (k.indexOf('node') === 0 || k.indexOf('install-') === 0) {
color = 'green';
var version = /(\d+\.)/.exec(engine);
if (version) {
version = version[0];
if (/^[0123]\./.test(version)) {
color = 'red';
}
}
pkg.engines[k] = {
version: engine,
title: k + ': ' + engine,
badgeURL: 'https://img.shields.io/badge/' + encodeURIComponent(k) +
'-' + encodeURIComponent(engine) + '-' + color + '.svg?style=flat-square',
};
if (engine === '*') {
color = 'red';
}
}
pkg.engines[k] = {
version: engine,
title: k + ': ' + engine,
badgeURL: config.badgeService.url(k, engine, { color }),
};
}
let packagephobiaSupport = downloadTotal >= config.packagephobiaMinDownloadCount;
if (pkg._publish_on_cnpm) {
pkg.isPrivate = true;
// need download total >= 1000
packagephobiaSupport = downloadTotal >= config.packagephobiaMinDownloadCount && config.packagephobiaSupportPrivatePackage;
} else {
pkg.isPrivate = false;
// add security check badge
pkg.snyk = {
badge: `${config.snykUrl}/test/npm/${pkg.name}/badge.svg`,
url: `${config.snykUrl}/test/npm/${pkg.name}`,
};
}
if (packagephobiaSupport) {
pkg.packagephobia = {
badge: `${config.packagephobiaURL}/badge?p=${pkg.name}@${pkg.version}`,
url: `${config.packagephobiaURL}/result?p=${pkg.name}@${pkg.version}`,
};
}
yield this.render('package', {
title: 'Package - ' + pkg.name,
package: pkg,
download: download
download: download,
});
};

View File

@@ -1,5 +1,5 @@
/**!
* cnpmjs.org - controllers/web/package/show_sync.js
* cnpmjs.org - controllers/web/show_sync.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
@@ -20,8 +20,15 @@ module.exports = function* showSync() {
if (!name) {
return this.redirect('/');
}
var type = 'package';
if (name.indexOf(':') > 0) {
var splits = name.split(':');
name = splits[1];
type = splits[0];
}
yield this.render('sync', {
type: type,
name: name,
title: 'Sync - ' + name,
title: 'Sync ' + type + ' - ' + name,
});
};

View File

@@ -1,24 +1,10 @@
/**!
* 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');
var he = require('he');
module.exports = function* showUser(next) {
var name = this.params.name;
@@ -29,15 +15,16 @@ module.exports = function* showUser(next) {
var packages = r[0];
var user = r[1];
if (!user && !packages.length) {
return yield* next;
return yield next;
}
user = user || {};
var data = {
name: name,
email: user.email,
json: user.json || {}
email: user.email ? he.encode(user.email, { encodeEverything: true }) : user.email,
json: user.json || {},
isNpmUser: user.isNpmUser,
};
if (data.json.login) {

View File

@@ -1,20 +1,5 @@
/**!
* cnpmjs.org - dispatch.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com>
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var childProcess = require('child_process');
var path = require('path');
var util = require('util');
@@ -23,6 +8,9 @@ var config = require('./config');
var workerPath = path.join(__dirname, 'worker.js');
var syncPath = path.join(__dirname, 'sync');
console.log('Starting cnpmjs.org ...\ncluster: %s\nadmins: %j\nscopes: %j\nsourceNpmRegistry: %s\nsyncModel: %s',
config.enableCluster, config.admins, config.scopes, config.sourceNpmRegistry, config.syncModel);
if (config.enableCluster) {
forkWorker();
if (config.syncModel !== 'none') {

30
docker-compose.yml Normal file
View File

@@ -0,0 +1,30 @@
version: '3'
services:
web:
build:
context: .
dockerfile: Dockerfile
image: cnpmjs.org
depends_on:
- mysql-db
volumes:
- cnpm-files-volume:/var/data/cnpm_data
ports:
- "7001:7001"
- "7002:7002"
mysql-db:
image: mysql:5.6
restart: always
environment :
MYSQL_USER: root
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
MYSQL_DATABASE: cnpmjs_test
MYSQL_ROOT_HOST: "%"
volumes:
- ./docs/db.sql:/docker-entrypoint-initdb.d/cnpm-init.sql
- cnpm-db-volume:/var/lib/mysql
volumes:
cnpm-files-volume:
cnpm-db-volume:

View File

@@ -0,0 +1,143 @@
# Migrating from 1.x to 2.x
2.x using [Sequelize] ORM to supports MySQL, MariaDB, SQLite or PostgreSQL databases.
## New download total table structure
### Create `downloads` table SQL
You should create `downloads` table first:
```sql
CREATE TABLE IF NOT EXISTS `downloads` (
`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',
`date` int unsigned NOT NULL COMMENT 'YYYYMM format',
`d01` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '01 download count',
`d02` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '02 download count',
`d03` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '03 download count',
`d04` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '04 download count',
`d05` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '05 download count',
`d06` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '06 download count',
`d07` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '07 download count',
`d08` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '08 download count',
`d09` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '09 download count',
`d10` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '10 download count',
`d11` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '11 download count',
`d12` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '12 download count',
`d13` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '13 download count',
`d14` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '14 download count',
`d15` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '15 download count',
`d16` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '16 download count',
`d17` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '17 download count',
`d18` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '18 download count',
`d19` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '19 download count',
`d20` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '20 download count',
`d21` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '21 download count',
`d22` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '22 download count',
`d23` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '23 download count',
`d24` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '24 download count',
`d25` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '25 download count',
`d26` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '26 download count',
`d27` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '27 download count',
`d28` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '28 download count',
`d29` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '29 download count',
`d30` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '30 download count',
`d31` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '31 download count',
PRIMARY KEY (`id`),
UNIQUE KEY `name_date` (`name`, `date`),
KEY `date` (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module download total info';
```
### Sync `download_total` to `downloads`
Then use [sync_download_total.js](../tools/sync_download_total.js) scrpt to sync datas from `download_total`:
```bash
$ node --harmony tools/sync_download_total.js
```
# `config.js` changes in 2.x
## New database config
```js
/**
* 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,
},
```
If you're still using MySQL and old config.js `mysqlServers: []` from 1.x:
```js
mysqlServers: [
{
host: '127.0.0.1',
port: 3306,
user: 'root',
password: ''
}
],
mysqlDatabase: 'cnpmjs_test',
mysqlMaxConnections: 4,
mysqlQueryTimeout: 5000,
```
We will do forward compat, and auto change old style config.js to:
```js
database: {
db: 'cnpmjs_test',
username: 'root',
password: '',
dialect: 'mysql',
host: '127.0.0.1',
port: 3306,
pool: {
maxConnections: 10,
minConnections: 0,
maxIdleTime: 30000
},
logging: !!process.env.SQL_DEBUG,
},
```
## remove `adaptScope`
`adaptScope: true | false` feature was removed.
[Sequelize]: http://sequelizejs.com/

View File

@@ -3,81 +3,97 @@ CREATE TABLE IF NOT EXISTS `user` (
`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,
`salt` varchar(100) NOT NULL COMMENT 'user salt',
`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',
`roles` varchar(200) NOT NULL DEFAULT '[]' COMMENT 'user roles',
`rev` varchar(40) NOT NULL COMMENT 'user rev',
`email` varchar(400) NOT NULL COMMENT 'user email',
`json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_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`)
UNIQUE KEY `uk_name` (`name`),
KEY `idx_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';
-- ALTER TABLE `user` CHANGE `json` `json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'json details';
-- ALTER TABLE `user`
-- CHANGE `salt` `salt` varchar(100) NOT NULL COMMENT 'user salt',
-- CHANGE `roles` `roles` varchar(200) NOT NULL DEFAULT '[]' COMMENT 'user roles',
-- CHANGE `rev` `rev` varchar(40) NOT NULL COMMENT 'user rev',
-- CHANGE `email` `email` varchar(400) NOT NULL COMMENT 'user email';
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,
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`description` longtext COMMENT 'module description',
PRIMARY KEY (`id`),
UNIQUE KEY `keyword_module_name` (`keyword`,`name`),
KEY `name` (`name`)
UNIQUE KEY `uk_keyword_module_name` (`keyword`,`name`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module keyword';
-- ALTER TABLE `module_keyword`
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
-- CHANGE `description` `description` longtext COMMENT 'module description';
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',
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
PRIMARY KEY (`id`),
UNIQUE KEY `user_module_name` (`user`,`name`),
KEY `name` (`name`)
UNIQUE KEY `uk_user_module_name` (`user`,`name`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module star';
-- ALTER TABLE `module_star`
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
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',
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
PRIMARY KEY (`id`),
UNIQUE KEY `user_module_name` (`user`,`name`),
KEY `name` (`name`)
UNIQUE KEY `uk_user_module_name` (`user`,`name`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='private module maintainers';
-- ALTER TABLE `module_maintainer`
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
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',
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
PRIMARY KEY (`id`),
UNIQUE KEY `user_module_name` (`user`,`name`),
KEY `name` (`name`)
UNIQUE KEY `uk_user_module_name` (`user`,`name`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='npm original module maintainers';
-- ALTER TABLE `npm_module_maintainer`
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
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',
`author` varchar(100) NOT NULL,
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`version` varchar(30) NOT NULL COMMENT 'module version',
`description` longtext,
`author` varchar(100) NOT NULL COMMENT 'module author',
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`version` varchar(70) NOT NULL COMMENT 'module version',
`description` longtext COMMENT 'module description',
`package` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'package.json',
`dist_shasum` varchar(100) DEFAULT NULL,
`dist_tarball` varchar(2048) DEFAULT NULL,
`dist_size` int(10) unsigned NOT NULL DEFAULT '0',
`publish_time` bigint(20) unsigned,
`dist_shasum` varchar(100) DEFAULT NULL COMMENT 'module dist SHASUM',
`dist_tarball` varchar(2048) DEFAULT NULL COMMENT 'module dist tarball',
`dist_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'module dist size',
`publish_time` bigint(20) unsigned COMMENT 'module publish time',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`,`version`),
KEY `gmt_modified` (`gmt_modified`),
KEY `publish_time` (`publish_time`),
KEY `author` (`author`)
UNIQUE KEY `uk_name` (`name`,`version`),
KEY `idx_gmt_modified` (`gmt_modified`),
KEY `idx_publish_time` (`publish_time`),
KEY `idx_author` (`author`),
KEY `idx_name_gmt_modified` (`name`,`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module info';
-- ALTER TABLE `module` ADD `description` longtext;
-- ALTER TABLE `module` ADD `publish_time` bigint(20) unsigned, ADD KEY `publish_time` (`publish_time`);
@@ -85,48 +101,97 @@ CREATE TABLE IF NOT EXISTS `module` (
-- ALTER TABLE `module` CHANGE `description` `description` LONGTEXT CHARACTER SET utf8 COLLATE utf8_general_ci;
-- show create table module\G
-- ALTER TABLE `module` CHANGE `name` `name` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
-- ALTER TABLE `module`
-- CHANGE `author` `author` varchar(100) NOT NULL COMMENT 'module author',
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
-- CHANGE `version` `version` varchar(70) NOT NULL COMMENT 'module version',
-- CHANGE `description` `description` longtext COMMENT 'module description',
-- CHANGE `package` `package` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'package.json',
-- CHANGE `dist_shasum` `dist_shasum` varchar(100) DEFAULT NULL COMMENT 'module dist SHASUM',
-- CHANGE `dist_tarball` `dist_tarball` varchar(2048) DEFAULT NULL COMMENT 'module dist tarball',
-- CHANGE `dist_size` `dist_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'module dist size',
-- CHANGE `publish_time` `publish_time` bigint(20) unsigned COMMENT 'module publish time';
CREATE TABLE IF NOT EXISTS `module_abbreviated` (
`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(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`version` varchar(70) NOT NULL COMMENT 'module version',
`package` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'the abbreviated metadata',
`publish_time` bigint(20) unsigned COMMENT 'the publish time',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_name` (`name`,`version`),
KEY `idx_gmt_modified` (`gmt_modified`),
KEY `idx_publish_time` (`publish_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module abbreviated info';
-- ALTER TABLE `module_abbreviated`
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
-- CHANGE `publish_time` `publish_time` bigint(20) unsigned COMMENT 'the publish time';
CREATE TABLE IF NOT EXISTS `package_readme` (
`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(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`readme` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'the latest version readme',
`version` varchar(70) NOT NULL COMMENT 'module version',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_name` (`name`),
KEY `idx_gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='package latest readme';
-- ALTER TABLE `package_readme`
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
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,
`username` varchar(100) NOT NULL COMMENT 'which username',
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`log` longtext COMMENT 'the raw log',
PRIMARY KEY (`id`),
KEY `name` (`name`)
KEY `idx_name` (`name`)
) 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';
-- ALTER TABLE `module_log`
-- CHANGE `username` `username` varchar(100) NOT NULL COMMENT 'which username',
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
-- CHANGE `log` `log` longtext COMMENT 'the raw log';
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',
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`tag` varchar(70) NOT NULL COMMENT 'tag name',
`version` varchar(70) NOT NULL COMMENT 'module version',
`module_id` bigint(20) unsigned NOT NULL COMMENT 'module id',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`, `tag`),
KEY `gmt_modified` (`gmt_modified`)
UNIQUE KEY `uk_name` (`name`, `tag`),
KEY `idx_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`);
-- ALTER TABLE `tag`
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
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',
`name` varchar(214) 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`)
UNIQUE KEY `uk_name` (`name`),
KEY `idx_gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module unpublished info';
-- ALTER TABLE `module_unpublished`
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
CREATE TABLE IF NOT EXISTS `total` (
`name` varchar(100) NOT NULL COMMENT 'total name',
`name` varchar(214) 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',
@@ -150,52 +215,121 @@ INSERT INTO total(name, gmt_modified) VALUES('total', now())
-- ALTER TABLE `total` ADD `fail_sync_num` int unsigned NOT NULL DEFAULT '0' COMMENT 'how many packages sync fail at this time'
-- 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';
-- ALTER TABLE `total` CHANGE `name` `name` varchar(214) NOT NULL COMMENT 'total name';
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 `date_name` (`date`, `name`)
-- 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` datetime 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 `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';
-- ALTER TABLE `download_total` CHANGE `date` `date` datetime NOT NULL COMMENT 'YYYY-MM-DD format';
CREATE TABLE IF NOT EXISTS `downloads` (
`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(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`date` int unsigned NOT NULL COMMENT 'YYYYMM format',
`d01` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '01 download count',
`d02` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '02 download count',
`d03` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '03 download count',
`d04` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '04 download count',
`d05` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '05 download count',
`d06` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '06 download count',
`d07` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '07 download count',
`d08` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '08 download count',
`d09` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '09 download count',
`d10` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '10 download count',
`d11` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '11 download count',
`d12` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '12 download count',
`d13` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '13 download count',
`d14` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '14 download count',
`d15` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '15 download count',
`d16` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '16 download count',
`d17` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '17 download count',
`d18` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '18 download count',
`d19` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '19 download count',
`d20` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '20 download count',
`d21` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '21 download count',
`d22` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '22 download count',
`d23` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '23 download count',
`d24` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '24 download count',
`d25` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '25 download count',
`d26` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '26 download count',
`d27` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '27 download count',
`d28` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '28 download count',
`d29` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '29 download count',
`d30` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '30 download count',
`d31` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '31 download count',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_name_date` (`name`, `date`),
KEY `idx_date` (`date`)
) 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';
-- ALTER TABLE `downloads`
-- CHANGE `name` `name` varchar(214) 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',
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`deps` varchar(214) 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`)
UNIQUE KEY `uk_name_deps` (`name`,`deps`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module deps';
-- ALTER TABLE `module_deps`
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
-- CHANGE `deps` `deps` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'which module depend on this module';
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 '/',
`name` varchar(214) NOT NULL COMMENT 'dir name',
`parent` varchar(214) 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`)
UNIQUE KEY `uk_name` (`parent`, `name`),
KEY `idx_gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='dist dir info';
-- ALTER TABLE `dist_dir`
-- CHANGE `name` `name` varchar(214) NOT NULL COMMENT 'dir name',
-- CHANGE `parent` `parent` varchar(214) NOT NULL COMMENT 'parent dir' DEFAULT '/';
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 '/',
`name` varchar(214) NOT NULL COMMENT 'file name',
`parent` varchar(214) 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`)
UNIQUE KEY `uk_fullname` (`parent`, `name`),
KEY `idx_gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='dist file info';
-- ALTER TABLE `dist_file`
-- CHANGE `name` `name` varchar(214) NOT NULL COMMENT 'file name',
-- CHANGE `parent` `parent` varchar(214) NOT NULL COMMENT 'parent dir' DEFAULT '/';
CREATE TABLE IF NOT EXISTS `token` (
`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',
`token` varchar(100) NOT NULL COMMENT 'token',
`user_id` varchar(100) NOT NULL COMMENT 'user name',
`readonly` tinyint NOT NULL DEFAULT 0 COMMENT 'readonly or not, 1: true, other: false',
`token_key` varchar(200) NOT NULL COMMENT 'token sha512 hash',
`cidr_whitelist` varchar(500) NOT NULL COMMENT 'ip list, ["127.0.0.1"]',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_token` (`token`),
KEY `idx_user` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='token info';

262
docs/dockerize/config.js Normal file
View File

@@ -0,0 +1,262 @@
'use strict';
var mkdirp = require('mkdirp');
var copy = require('copy-to');
var path = require('path');
var fs = require('fs');
var os = require('os');
var version = require('../package.json').version;
var root = path.dirname(__dirname);
var dataDir = process.env.CNPM_DATA_DIR ;
var config = {
version: version,
dataDir: dataDir,
/**
* Cluster mode
*/
enableCluster: false,
numCPUs: os.cpus().length,
/*
* server configure
*/
registryPort: 7001,
webPort: 7002,
bindingHost: '0.0.0.0', // binding on 0.0.0.0 for outside of container access
// debug mode
// if in debug mode, some middleware like limit wont load
// logger module will print to stdout
debug: process.env.NODE_ENV === 'development',
// page mode, enable on development env
pagemock: process.env.NODE_ENV === 'development',
// session secret
sessionSecret: 'cnpmjs.org test session secret',
// max request json body size
jsonLimit: '10mb',
// log dir name
logdir: path.join(dataDir, 'logs'),
// update file template dir
uploadDir: path.join(dataDir, 'downloads'),
// 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: {
enable: false,
appname: 'cnpmjs.org',
from: 'cnpmjs.org mail sender <adderss@gmail.com>',
service: 'gmail',
auth: {
user: 'address@gmail.com',
pass: 'your password'
}
},
logoURL: 'https://os.alipayobjects.com/rmsportal/oygxuIUkkrRccUz.jpg', // cnpm logo image url
adBanner: '',
customReadmeFile: '', // you can use your custom readme file instead the cnpm one
customFooter: '', // you can add copyright and site total script html here
npmClientName: 'cnpm', // use `${name} install package`
packagePageContributorSearch: true, // package page contributor link to search, default is true
// max handle number of package.json `dependencies` property
maxDependencies: 200,
// backup filepath prefix
backupFilePrefix: '/cnpm/backup/',
/**
* database config
*/
database: {
db: 'cnpmjs_test',
username: 'root',
password: '',
// the sql dialect of the database
// - currently supported: 'mysql', 'sqlite', 'postgres', 'mariadb'
dialect: 'mysql',
// the Docker container network hostname defined at docker-compose.yml
host: 'mysql-db',
// 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/data.sqlite
//storage: path.join(dataDir, 'data.sqlite'),
logging: !!process.env.SQL_DEBUG,
},
// package tarball store in local filesystem by default
nfs: require('fs-cnpm')({
dir: path.join(dataDir, 'nfs')
}),
// if set true, will 302 redirect to `nfs.url(dist.key)`
downloadRedirectToNFS: false,
// registry url name
registryHost: '127.0.0.1:7001',
/**
* registry mode config
*/
// enable private mode or not
// private mode: only admins can publish, other users just can sync package from source npm
// public mode: all users can publish
enablePrivate: false,
// registry scopes, if don't set, means do not support scopes
scopes: [ '@cnpm', '@cnpmtest', '@cnpm-test' ],
// some registry already have some private packages in global scope
// but we want to treat them as scoped private packages,
// so you can use this white list.
privatePackages: [],
/**
* sync configs
*/
// 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.com',
officialNpmReplicate: 'https://replicate.npmjs.com',
// sync source, upstream registry
// If you want to directly sync from official npm's registry
// please drop them an email first
sourceNpmRegistry: 'https://registry.npm.taobao.org',
// upstream registry is base on cnpm/cnpmjs.org or not
// if your upstream is official npm registry, please turn it off
sourceNpmRegistryIsCNpm: true,
// if install return 404, try to sync from source registry
syncByInstall: true,
// sync mode select
// none: do not sync any module, proxy all public modules from sourceNpmRegistry
// 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,
// changes streaming sync
syncChangesStream: false,
handleSyncRegistry: 'http://127.0.0.1:7001',
// badge subject on http://shields.io/
badgePrefixURL: 'https://img.shields.io/badge',
badgeSubject: 'cnpm',
// custom user service, @see https://github.com/cnpm/cnpmjs.org/wiki/Use-Your-Own-User-Authorization
// when you not intend to ingegrate with your company's user system, then use null, it would
// use the default cnpm user system
userService: null,
// always-auth https://docs.npmjs.com/misc/config#always-auth
// Force npm to always require authentication when accessing the registry, even for GET requests.
alwaysAuth: false,
// if you're behind firewall, need to request through http proxy, please set this
// e.g.: `httpProxy: 'http://proxy.mycompany.com:8080'`
httpProxy: null,
// snyk.io root url
snykUrl: 'https://snyk.io',
// https://github.com/cnpm/cnpmjs.org/issues/1149
// if enable this option, must create module_abbreviated and package_readme table in database
enableAbbreviatedMetadata: true,
// global hook function: function* (envelope) {}
// envelope format please see https://github.com/npm/registry/blob/master/docs/hooks/hooks-payload.md#payload
globalHook: null,
accelerateHostMap: {},
};
if (process.env.NODE_ENV !== 'test') {
var customConfig;
if (process.env.NODE_ENV === 'development') {
customConfig = path.join(root, 'config', 'config.js');
} else {
// 1. try to load `$dataDir/config.json` first, not exists then goto 2.
// 2. load config/config.js, everything in config.js will cover the same key in index.js
customConfig = path.join(dataDir, 'config.json');
if (!fs.existsSync(customConfig)) {
customConfig = path.join(root, 'config', 'config.js');
}
}
if (fs.existsSync(customConfig)) {
copy(require(customConfig)).override(config);
}
}
mkdirp.sync(config.logdir);
mkdirp.sync(config.uploadDir);
module.exports = config;
config.loadConfig = function (customConfig) {
if (!customConfig) {
return;
}
copy(customConfig).override(config);
};

BIN
docs/network.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

98
docs/network.puml Normal file
View File

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

View File

@@ -9,6 +9,8 @@
* [User](/docs/registry-api.md#user)
* [Search](/docs/registry-api.md#search)
[![Run in Postman](https://run.pstmn.io/button.svg)](https://app.getpostman.com/run-collection/f6c8cb46358039bcd689#?env%5BRegistry%5D=W3sia2V5IjoicmVnaXN0cnkiLCJ0eXBlIjoidGV4dCIsInZhbHVlIjoiaHR0cHM6Ly9yZWdpc3RyeS5ucG0udGFvYmFvLm9yZyIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoicGFja2FnZSIsInZhbHVlIjoiY25wbSIsInR5cGUiOiJ0ZXh0IiwiZW5hYmxlZCI6dHJ1ZX1d)
## Schema
All API access is over HTTPS or HTTP,
@@ -19,22 +21,20 @@ All data is sent and received as JSON.
$ 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}
{
"db_name": "registry",
"doc_count": 123772,
"doc_del_count": 377,
"update_seq": 685591,
"purge_seq": 0,
"compact_running": false,
"disk_size": 634187899,
"data_size": 445454185,
"instance_start_time": "1420670152481614",
"disk_format_version": 6,
"committed_update_seq": 685591
}
```
## Client Errors
@@ -50,7 +50,7 @@ Status: 4xx
## Authentication
There is only one way to authenticate through the API.
There are two ways to authenticate through the API.
## Basic Authentication
@@ -58,25 +58,19 @@ There is only one way to authenticate through the API.
$ curl -u "username:password" https://registry.npmjs.org
```
## Bearer Authentication
```bash
$ curl -H "Authorization: Bearer ${UUId}" 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
$ curl -i -X "GET" -u "foo:pwd" \
"https://registry.npmjs.com/-/user/org.couchdb.user:npm-user-service-testuser?write=true"
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."}
```
@@ -101,14 +95,11 @@ X-Timer: S1407252805.261390,VS0,VE434
GET /:package
```
#### Response
#### Response 200
```json
HTTP/1.1 200 OK
Etag: "8UDCP753LFXOG42NMX88JAN40"
Content-Type: application/json
Cache-Control: max-age=60
Content-Length: 2243
{
"_id": "pedding",
@@ -246,6 +237,17 @@ Content-Length: 2243
}
```
#### Response 404
```json
HTTP/1.1 404 Object Not Found
{
"error": "not_found",
"reason": "document not found"
}
```
### ~~Get a special version or tag package~~
__deprecated__
@@ -254,7 +256,7 @@ __deprecated__
GET /:package/:tag_or_version
```
#### Reponse
#### Reponse 200
```json
HTTP/1.1 200 OK
@@ -745,9 +747,52 @@ HTTP/1.1 200 OK
## 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)
- [Auth user](/docs/registry-api.md#auth-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)
### Auth user
* Authentication required.
```
GET /-/user/org.couchdb.user::username?write=true
```
#### Response 200
```json
HTTP/1.1 200 OK
ETag: "5-a31b61ba3c50b50f7fcaf185e079e17a"
{
"_id": "org.couchdb.user:npm-user-service-testuser",
"_rev": "5-a31b61ba3c50b50f7fcaf185e079e17a",
"password_scheme": "pbkdf2",
"iterations": 10,
"name": "npm-user-service-testuser",
"email": "fengmk2@gmail.com",
"type": "user",
"roles": [],
"date": "2015-01-04T08:28:51.378Z",
"password_scheme": "pbkdf2",
"iterations": 10,
"derived_key": "644157c126b93356e6eba2c59fdf1b7ec644ebf2",
"salt": "5d13874c0aa10751e35743bacd6eedd5"
}
```
#### Response 401
```json
HTTP/1.1 401 Unauthorized
{
"error": "unauthorized",
"reason": "Name or password is incorrect."
}
```
### Get a single user
@@ -755,7 +800,7 @@ HTTP/1.1 200 OK
GET /-/user/org.couchdb.user::username
```
#### Response
#### Response 200
```json
HTTP/1.1 200 OK
@@ -825,6 +870,17 @@ ETag: "32-984ee97e01aea166dcab6d1517c730e3"
}
```
#### Response 404
```json
HTTP/1.1 404 Object Not Found
{
"error": "not_found",
"reason": "missing"
}
```
### Add a new user
```
@@ -845,7 +901,7 @@ PUT /-/user/org.couchdb.user::username
}
```
#### Response
#### Response 201
```json
Status: 201 Created
@@ -853,7 +909,21 @@ Status: 201 Created
{
"ok": true,
"id": "org.couchdb.user:fengmk2",
"rev": "32-984ee97e01aea166dcab6d1517c730e3"
"rev": "32-984ee97e01aea166dcab6d1517c730e3",
"token": "85d32fad-bd43-4dd7-9451-4f7d907313a2"
}
```
#### Response 409
User already exists
```json
HTTP/1.1 409 Conflict
{
"error": "conflict",
"reason": "Document update conflict."
}
```
@@ -893,3 +963,76 @@ Status: 201 Created
```
## Search
## Token
- [Create token](/docs/registry-api.md#create-token)
- [List token](/docs/registry-api.md#list-token)
- [Delete token](/docs/registry-api.md#delete-token)
### Create token
* Authentication required.
```
POST /-/npm/v1/tokens
```
#### Input
```json
{
"password": "123",
"readonly": false,
"cidr_whitelist": [
"127.0.0.1"
]
}
```
#### Response 200
```json
HTTP/1.1 200 OK
{
"token": "85d32fad-bd43-4dd7-9451-4f7d907313a2",
"key": "d06309a210570ef71cd9c7bd4849e7e96eeaa841976e63326436f6fd320dc4bbd452710e4e0fedc2efc2ea4a793b7159e95e9596e85e00dee26adc3f8afbb97f",
"cidr_whitelist": [ "127.0.0.1" ],
"created": "2015-01-04T08:28:51.378Z",
"updated": "2015-01-04T08:28:51.378Z",
"readonly": false
}
```
### List token
* Authentication required.
```
GET /-/npm/v1/tokens
```
### Input
perPage=10&page=0
#### Response 200
```json
{
"objects": [{
"token": "85d32f...7313a2",
"key": "d06309a210570ef71cd9c7bd4849e7e96eeaa841976e63326436f6fd320dc4bbd452710e4e0fedc2efc2ea4a793b7159e95e9596e85e00dee26adc3f8afbb97f",
"cidr_whitelist": [ "127.0.0.1" ],
"created": "2015-01-04T08:28:51.378Z",
"updated": "2015-01-04T08:28:51.378Z",
"readonly": false
}]
}
```
### Delete token
* Authentication required.
```
GET /-/npm/v1/tokens/token/:UUID
```
#### Response 204

View File

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

View File

@@ -4,12 +4,13 @@ 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/)
- Our public registry: [r.cnpmjs.org](https://r.cnpmjs.org), syncing from [registry.npmjs.com](https://registry.npmjs.com)
- [cnpmjs.org](/) version: <span id="app-version"></span>
- [Node.js](https://nodejs.org) version: <span id="node-version"></span>
- For developers in China, please visit [the China mirror](https://npm.taobao.org). 中国用户请访问[国内镜像站点](https://npm.taobao.org)
- Use the private npm service provided by Alibaba Cloud DevOps which build with cnpm. [https://packages.aliyun.com/](https://packages.aliyun.com/?channel=pd_cnpm_github)
<div class="ant-table">
<table class="downloads">
<tbody>
<tr>
@@ -29,12 +30,17 @@ So `cnpm` is meaning: **Company npm**.
</tr>
</tbody>
</table>
</div>
<div class="sync" style="display:none;">
<h3>Sync Status</h3>
<p id="sync-model"></p>
<p>Last sync time is <span id="last-sync-time"></span>. </p>
<p class="syncing alert alert-info">The sync worker is working in the backend now. </p>
<div class="ant-alert ant-alert-info syncing">
<span class="anticon ant-alert-icon anticon-info-circle"></span>
<span class="ant-alert-description">The sync worker is working in the backend now. </span>
</div>
<div class="ant-table">
<table class="sync-status">
<tbody>
<tr>
@@ -49,50 +55,55 @@ So `cnpm` is meaning: **Company npm**.
</tr>
</tbody>
</table>
</div>
</div>
Running on [Node.js](http://nodejs.org), version <span id="node-version"></span>.
<script src="/js/readme.js"></script>
## Version Badge
## Badges
Default style is `flat-square`.
Badge URL: `http://cnpmjs.org/badge/v/cnpmjs.org.svg` ![cnpmjs.org-badge](http://cnpmjs.org/badge/v/cnpmjs.org.svg)
### Version
Badge URL: `https://cnpmjs.org/badge/v/cnpmjs.org.svg` ![cnpmjs.org-version-badge](//cnpmjs.org/badge/v/cnpmjs.org.svg)
* `<0.1.0 & >=0.0.0`: ![red-badge](https://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)
### Downloads
Badge URL: `https://cnpmjs.org/badge/d/cnpmjs.org.svg` ![cnpmjs.org-download-badge](//cnpmjs.org/badge/d/cnpmjs.org.svg)
## Usage
use our npm client [cnpm](https://github.com/cnpm/cnpm)(More suitable with cnpmjs.org and gzip support), you can get our client through npm:
```
npm install -g cnpm --registry=http://r.cnpmjs.org
```bash
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
```
Or you can alias NPM to use it:
```bash
alias cnpm="npm --registry=http://r.cnpmjs.org \
alias cnpm="npm --registry=https://registry.npm.taobao.org \
--cache=$HOME/.npm/.cache/cnpm \
--disturl=http://cnpmjs.org/dist \
--disturl=https://npm.taobao.org/mirrors/node \
--userconfig=$HOME/.cnpmrc"
#Or alias it in .bashrc or .zshrc
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=http://r.cnpmjs.org \
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=https://registry.npm.taobao.org \
--cache=$HOME/.npm/.cache/cnpm \
--disturl=http://cnpmjs.org/dist \
--disturl=https://npm.taobao.org/mirrors/node \
--userconfig=$HOME/.cnpmrc"' >> ~/.zshrc && source ~/.zshrc
```
### install
Install package from [r.cnpmjs.org](http://r.cnpmjs.org). When installing a package or version does not exist, it will try to install from the official registry([registry.npmjs.org](http://registry.npmjs.org)), and sync this package to cnpm in the backend.
Install package from [r.cnpmjs.org](//r.cnpmjs.org). When installing a package or version does not exist, it will try to install from the official registry([registry.npmjs.org](https://registry.npmjs.org)), and sync this package to cnpm in the backend.
```
```bash
$ cnpm install [name]
```
@@ -104,10 +115,10 @@ Only `cnpm` cli has this command. Meaning sync package from source npm.
$ cnpm sync connect
```
sync package on web: [cnpmjs.org/sync/connect](http://cnpmjs.org/sync/connect)
sync package on web: [sync/connect](/sync/connect)
```bash
$ open http://cnpmjs.org/sync/connect
$ open http://registry.npm.taobao.org/sync/connect
```
### publish / unpublish
@@ -135,6 +146,11 @@ $ cnpm info cnpm
Release [History](/history).
## npm and cnpm relation
## npmjs.org, cnpmjs.org and npm.taobao.org relation
![npm&cnpm](https://docs.google.com/drawings/d/12QeQfGalqjsB77mRnf5Iq5oSXHCIUTvZTwECMonqCmw/pub?w=383&h=284)
![npm&cnpm](https://cloud.githubusercontent.com/assets/543405/21505401/fd0b6220-cca1-11e6-86ed-599cc81bb03b.png)
## Sponsors
- [![阿里云](https://static.aliyun.com/images/www-summerwind/logo.gif)](http://click.aliyun.com/m/4288/) [![阿里云云效](https://img.alicdn.com/tfs/TB116yt3fb2gK0jSZK9XXaEgFXa-106-20.png)](https://devops.aliyun.com/?channel=pd_cnpm_github) (2016.2 - now)
- [![UCloud云计算](https://www.ucloud.cn/static/style/images/about/logo.png)](http://www.ucloud.cn?sem=sdk-CNPMJS) (2015.3 - 2016.3)

View File

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

View File

@@ -1,41 +1,82 @@
/**!
* cnpmjs.org - lib/common.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var crypto = require('crypto');
var path = require('path');
var config = require('../config');
var utility = require('utility');
var util = require('util');
var config = require('../config');
var BASIC_PREFIX = /basic /i;
var BEARER_PREFIX = /bearer /i;
exports.getTarballFilepath = function (filename) {
exports.getTarballFilepath = function (packageName, packageVersion, 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');
var name = filename.replace(/\.tgz$/, '.' + crypto.randomBytes(16).toString('hex'));
// use filename string md5 instead, fix "ENAMETOOLONG: name too long" error
name = packageName.replace(/\//g, '-').replace(/\@/g, '') + '-' + packageVersion.substring(0, 20) + '.' + utility.md5(name) + '.tgz';
return path.join(config.uploadDir, name);
};
exports.getCDNKey = function (name, filename) {
// if name is scope package name, need to auto fix filename as a scope package file name
// e.g.: @scope/foo, filename: foo-1.0.0.tgz => filename: @scope/foo-1.0.0.tgz
if (name[0] === '@' && filename[0] !== '@') {
filename = name.split('/')[0] + '/' + filename;
}
return '/' + name + '/-/' + filename;
};
exports.getUnpublishFileKey = function (name) {
return `/${name}/sync/unpublish/unpublish-package.json`;
};
exports.getPackageFileCDNKey = function (name, version) {
return `/${name}/sync/packages/package-${version}.json`;
};
exports.getDistTagCDNKey = function (name, tag) {
return `/${name}/sync/tags/tag-${tag}.json`;
};
exports.getSyncTagDir = function (name) {
return `${name}/sync/tags/`;
};
exports.getSyncPackageDir = function (name) {
return `${name}/sync/packages/`;
};
const TAG_NAME_REG = /^tag-(.+)\.json$/;
exports.getTagNameFromFileName = function (fileName) {
const res = fileName.match(TAG_NAME_REG);
return res && res[1];
};
exports.isBackupTagFile = function (fileName) {
return TAG_NAME_REG.test(fileName);
};
const PACKAGE_NAME_REG = /^package-(.+)\.json$/;
exports.getVersionFromFileName = function (fileName) {
const res = fileName.match(PACKAGE_NAME_REG);
return res && res[1];
};
exports.isBackupPkgFile = function (fileName) {
return PACKAGE_NAME_REG.test(fileName);
};
exports.setDownloadURL = function (pkg, ctx, host) {
if (pkg.dist) {
host = host || ctx.host;
host = host || config.registryHost || ctx.host;
var protocol = config.protocol || ctx.protocol;
pkg.dist.tarball = util.format('%s://%s/%s/download/%s-%s.tgz',
ctx.protocol,
protocol,
host, pkg.name, pkg.name, pkg.version);
if (ctx.querystring) {
var backupUrl = pkg.dist.tarball;
pkg.dist.tarball += '?' + ctx.querystring + '&other_urls=' + encodeURIComponent(backupUrl);
}
}
};
@@ -56,3 +97,38 @@ exports.isMaintainer = function (user, maintainers) {
return match.length > 0;
};
exports.isLocalModule = function (mods) {
for (var i = 0; i < mods.length; i++) {
var r = mods[i];
if (r.package && r.package._publish_on_cnpm) {
return true;
}
}
return false;
};
exports.isPrivateScopedPackage = function (name) {
if (!name) {
return false;
}
if (name[0] !== '@') {
return false;
}
return config.scopes.indexOf(name.split('/')[0]) >= 0;
};
var AuthorizeType = exports.AuthorizeType = {
BASIC: 'BASIC',
BEARER: 'BEARER',
};
exports.getAuthorizeType = function (ctx) {
var authorization = (ctx.get('authorization') || '').trim();
if (BASIC_PREFIX.test(authorization)) {
return AuthorizeType.BASIC;
} else if (BEARER_PREFIX.test(authorization)) {
return AuthorizeType.BEARER;
}
};

View File

@@ -1,21 +1,10 @@
/**!
* cnpmjs.org - middleware/auth.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:middleware:auth');
var UserService = require('../services/user');
var TokenService = require('../services/token');
var config = require('../config');
var common = require('../lib/common');
/**
* Parse the request authorization
@@ -26,24 +15,23 @@ module.exports = function () {
return function* auth(next) {
this.user = {};
var authorization = (this.get('authorization') || '').split(' ')[1] || '';
authorization = authorization.trim();
var authorization = (this.get('authorization') || '').trim();
debug('%s %s with %j', this.method, this.url, authorization);
if (!authorization) {
return yield* next;
return yield unauthorized.call(this, next);
}
authorization = new Buffer(authorization, 'base64').toString().split(':');
if (authorization.length !== 2) {
return yield* next;
}
var username = authorization[0];
var password = authorization[1];
var row;
try {
row = yield* UserService.auth(username, password);
var authorizeType = common.getAuthorizeType(this);
if (authorizeType === common.AuthorizeType.BASIC) {
row = yield basicAuth(authorization);
} else if (authorizeType === common.AuthorizeType.BEARER) {
row = yield bearerAuth(authorization, this.method, this.ip);
} else {
return yield unauthorized.call(this, next);
}
} catch (err) {
// do not response error here
// many request do not need login
@@ -52,13 +40,54 @@ module.exports = function () {
if (!row) {
debug('auth fail user: %j, headers: %j', row, this.header);
return yield* next;
return yield unauthorized.call(this, next);
}
this.user.name = row.login;
this.user.isAdmin = row.site_admin;
this.user.scopes = row.scopes;
debug('auth pass user: %j, headers: %j', this.user, this.header);
yield* next;
yield next;
};
};
function* basicAuth(authorization) {
authorization = authorization.split(' ')[1];
authorization = Buffer.from(authorization, 'base64').toString();
var pos = authorization.indexOf(':');
if (pos === -1) {
return null;
}
var username = authorization.slice(0, pos);
var password = authorization.slice(pos + 1);
return yield UserService.auth(username, password);
}
function* bearerAuth(authorization, method, ip) {
var token = authorization.split(' ')[1];
var isReadOperation = method === 'HEAD' || method === 'GET';
return yield TokenService.validateToken(token, {
isReadOperation: isReadOperation,
accessIp: ip,
});
}
function* unauthorized(next) {
if (!config.alwaysAuth || this.method !== 'GET') {
return yield next;
}
this.status = 401;
this.set('WWW-Authenticate', 'Basic realm="sample"');
if (this.accepts(['html', 'json']) === 'json') {
const error = '[unauthorized] login first';
this.body = {
error,
reason: error,
};
} else {
this.body = 'login first';
}
}

View File

@@ -1,19 +1,5 @@
/**!
* cnpmjs.org - middleware/block.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
module.exports = function () {
return function* block(next) {
var ua = String(this.get('user-agent')).toLowerCase();
@@ -23,6 +9,6 @@ module.exports = function () {
message: 'forbidden Ruby user-agent, ip: ' + this.ip
};
}
yield* next;
yield next;
};
};

View File

@@ -1,19 +1,5 @@
/**!
* 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
@@ -22,11 +8,11 @@ module.exports = function* editable(next) {
var moduleName = this.params.name || this.params[0];
if (username && moduleName) {
if (this.user.isAdmin) {
return yield* next;
return yield next;
}
var isMaintainer = yield* packageService.isMaintainer(moduleName, username);
var isMaintainer = yield packageService.isMaintainer(moduleName, username);
if (isMaintainer) {
return yield* next;
return yield next;
}
}
@@ -35,8 +21,9 @@ module.exports = function* editable(next) {
if (username) {
message = username + ' ' + message;
}
message = '[forbidden] ' + message;
this.body = {
error: 'forbidden user',
reason: message
error: message,
reason: message,
};
};

View File

@@ -0,0 +1,17 @@
'use strict';
var packageService = require('../services/package');
module.exports = function* (next) {
var name = this.params.name || this.params[0];
var pkg = yield packageService.getLatestModule(name);
if (pkg) {
return yield next;
}
this.status = 404;
const error = '[not_found] document not found';
this.body = {
error,
reason: error,
};
};

View File

@@ -1,34 +1,14 @@
/**!
* 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;
module.exports = function* ignoreLimit(next) {
yield next;
};
} else {
if (!config.debug) {
limitConfig.store = store;
}
module.exports = limit(limitConfig);
}

View File

@@ -1,42 +1,35 @@
/*!
* cnpmjs.org - middleware/login.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var http = require('http');
module.exports = function *login(next) {
if (this.path === '/-/ping' && this.query.write !== 'true') {
yield next;
return;
}
if (this.user.error) {
var status = this.user.error.status;
this.status = http.STATUS_CODES[status]
? status
: 500;
const error = `[${this.user.error.name}] ${this.user.error.message}`;
this.body = {
error: this.user.error.name,
reason: this.user.error.message
error,
reason: error,
};
return;
}
if (!this.user.name) {
this.status = 401;
const error = '[unauthorized] Login first';
this.body = {
error: 'unauthorized',
reason: 'Login first.'
error,
reason: error,
};
return;
}
yield *next;
yield next;
};

View File

@@ -1,19 +1,5 @@
/**!
* cnpmjs.org - middleware/opensearch.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 template = '<?xml version="1.0" encoding="UTF-8"?>\
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">\
<ShortName>CNPM</ShortName>\
@@ -22,11 +8,13 @@ var template = '<?xml version="1.0" encoding="UTF-8"?>\
<Url method="get" type="text/html" template="http://${host}/browse/keyword/{searchTerms}"/>\
</OpenSearchDescription>';
module.exports = function *publishable(next) {
var config = require('../config');
module.exports = function* opensearch(next) {
if (this.path === '/opensearch.xml') {
this.type = 'text/xml';
this.charset = 'utf-8';
this.body = template.replace('${host}', this.host);
this.body = template.replace('${host}', config.opensearch.host || this.host);
}
yield *next;
yield next;
};

View File

@@ -0,0 +1,84 @@
'use strict';
var debug = require('debug')('cnpmjs.org:middleware:proxy_to_npm');
var config = require('../config');
module.exports = function (options) {
var redirectUrl = config.sourceNpmRegistry;
var proxyUrls = [
// /:pkg, dont contains scoped package
// /:pkg/:versionOrTag
/^\/[\w\-\.]+(?:\/[\w\-\.]+)?$/,
// /-/package/:pkg/dist-tags
/^\/\-\/package\/[\w\-\.]+\/dist-tags/,
];
var scopedUrls = [
// scoped package
/^\/(@[\w\-\.]+)\/[\w\-\.]+(?:\/[\w\-\.]+)?$/,
/^\/\-\/package\/(@[\w\-\.]+)\/[\w\-\.]+\/dist\-tags/,
];
if (options && options.isWeb) {
redirectUrl = config.sourceNpmWeb || redirectUrl.replace('//registry.', '//');
proxyUrls = [
// /package/:pkg
/^\/package\/[\w\-\.]+/,
];
scopedUrls = [
// scoped package
/^\/package\/(@[\w\-\.]+)\/[\w\-\.]+/,
];
}
return function* proxyToNpm(next) {
if (config.syncModel !== 'none') {
return yield next;
}
// syncModel === none
// only proxy read requests
if (this.method !== 'GET' && this.method !== 'HEAD') {
return yield next;
}
var pathname = decodeURIComponent(this.path);
var isScoped = false;
var isPublichScoped = false;
// check scoped name
if (config.scopes && config.scopes.length > 0) {
for (var i = 0; i < scopedUrls.length; i++) {
const m = scopedUrls[i].exec(pathname);
if (m) {
isScoped = true;
if (config.scopes.indexOf(m[1]) !== -1) {
// internal scoped
isPublichScoped = false;
} else {
isPublichScoped = true;
}
break;
}
}
}
var isPublich = false;
if (!isScoped) {
for (var i = 0; i < proxyUrls.length; i++) {
isPublich = proxyUrls[i].test(pathname);
if (isPublich) {
break;
}
}
}
if (isPublich || isPublichScoped) {
var url = redirectUrl + this.url;
debug('proxy isPublich: %s, isPublichScoped: %s, package to %s',
isPublich, isPublichScoped, url);
this.redirect(url);
return;
}
yield next;
};
};

View File

@@ -1,57 +1,68 @@
/**!
* cnpmjs.org - middleware/publishable.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var packageService = require('../services/package');
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) {
// admins always can publish and unpublish
if (this.user.isAdmin) {
return yield next;
}
// private mode, normal user can't publish and unpublish
if (config.enablePrivate) {
this.status = 403;
const error = '[no_perms] Private mode enable, only admin can publish this module';
this.body = {
error: 'no_perms',
reason: 'Private mode enable, only admin can publish this module'
error,
reason: error,
};
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
// public mode, normal user have permission to publish `scoped package`
// and only can publish with scopes in `ctx.user.scopes`, default is `config.scopes`
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;
return yield next;
}
// scope
// check the package is already exists and is maintainer
var result = yield packageService.authMaintainer(name, this.user.name);
if (result.maintainers && result.maintainers.length) {
if (result.isMaintainer) {
return yield next;
}
this.status = 403;
const error = '[forbidden] ' + this.user.name + ' not authorized to modify ' + name +
', please contact maintainers: ' + result.maintainers.join(', ');
this.body = {
error,
reason: error,
};
return;
}
// scoped module
if (name[0] === '@') {
if (checkScope(name, this)) {
return yield* next;
return yield next;
}
return;
}
// none-scope
if (checkNoneScope(this)) {
return yield* next;
}
assertNoneScope(name, this);
};
/**
@@ -68,9 +79,10 @@ function checkScope(name, ctx) {
if (ctx.user.scopes.indexOf(scope) === -1) {
debug('assert scope %s error', name);
ctx.status = 400;
const error = util.format('[invalid] scope %s not match legal scopes: %s', scope, ctx.user.scopes.join(', '));
ctx.body = {
error: 'invalid scope',
reason: util.format('scope %s not match legal scopes: %s', scope, ctx.user.scopes.join(', '))
error,
reason: error,
};
return false;
}
@@ -82,21 +94,20 @@ function checkScope(name, ctx) {
* 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;
}
function assertNoneScope(name, ctx) {
ctx.status = 403;
if (ctx.user.scopes.length === 0) {
const error = '[no_perms] can\'t publish non-scoped package, please set `config.scopes`';
ctx.body = {
error,
reason: error,
};
return;
}
const error = '[no_perms] only allow publish with ' + ctx.user.scopes.join(', ') + ' scope(s)';
ctx.body = {
error: 'no_perms',
reason: 'only allow publish with ' + ctx.user.scopes.join(', ') + ' scope(s)'
error,
reason: error,
};
}

View File

@@ -1,21 +1,7 @@
/**!
* 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;
yield next;
if (this.status && this.status !== 404) {
return;
@@ -25,8 +11,9 @@ module.exports = function* notFound(next) {
}
this.status = 404;
const error = '[not_found] document not found';
this.body = {
error: 'not_found',
reason: 'document not found'
error,
reason: error,
};
};

View File

@@ -23,7 +23,8 @@ 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: {
alias: {
'/favicon.ico': '/favicon.png'
}
},
gzip: config.enableCompress,
});

View File

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

View File

@@ -0,0 +1,15 @@
'use strict';
module.exports = function *unpublishable(next) {
// only admin user can unpublish
if (!this.user.isAdmin) {
this.status = 403;
const error = '[no_perms] Only administrators can unpublish module';
this.body = {
error,
reason: error,
};
return;
}
yield next;
};

View File

@@ -1,23 +1,9 @@
/**!
* 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;
yield next;
if (this.status && this.status !== 404) {
return;
@@ -46,7 +32,7 @@ module.exports = function* notFound(next) {
}
this.status = 404;
yield* this.render('404', {
yield this.render('404', {
title: title,
name: name
});

View File

@@ -1,19 +1,5 @@
/**!
* 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.
*/
/**
* list all module names by user
* @param {String} user
@@ -134,7 +120,7 @@ exports.updateMaintainers = function* (name, users) {
remove: []
};
}
var exists = yield* this.listMaintainers(name);
var exists = yield this.listMaintainers(name);
var addUsers = users.filter(function (username) {
// add user which in `users` but do not in `exists`
@@ -156,4 +142,3 @@ exports.updateMaintainers = function* (name, users) {
remove: removeUsers
};
};

View File

@@ -1,64 +0,0 @@
/**!
* 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: {
}
});
};

View File

@@ -1,82 +0,0 @@
/**!
* 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: {
}
});
};

View File

@@ -14,42 +14,258 @@
* Module dependencies.
*/
// CREATE TABLE IF NOT EXISTS `download_total` (
// CREATE TABLE IF NOT EXISTS `downloads` (
// `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',
// `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
// `date` int unsigned NOT NULL COMMENT 'YYYYMM format',
// `d01` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '01 download count',
// `d02` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '02 download count',
// `d03` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '03 download count',
// `d04` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '04 download count',
// `d05` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '05 download count',
// `d06` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '06 download count',
// `d07` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '07 download count',
// `d08` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '08 download count',
// `d09` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '09 download count',
// `d10` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '10 download count',
// `d11` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '11 download count',
// `d12` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '12 download count',
// `d13` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '13 download count',
// `d14` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '14 download count',
// `d15` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '15 download count',
// `d16` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '16 download count',
// `d17` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '17 download count',
// `d18` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '18 download count',
// `d19` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '19 download count',
// `d20` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '20 download count',
// `d21` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '21 download count',
// `d22` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '22 download count',
// `d23` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '23 download count',
// `d24` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '24 download count',
// `d25` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '25 download count',
// `d26` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '26 download count',
// `d27` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '27 download count',
// `d28` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '28 download count',
// `d29` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '29 download count',
// `d30` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '30 download count',
// `d31` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '31 download count',
// PRIMARY KEY (`id`),
// UNIQUE KEY `download_total_date_name` (`date`, `name`)
// UNIQUE KEY `uk_name_date` (`name`, `date`),
// KEY `idx_date` (`date`)
// ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module download total info';
const config = require('../config');
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),
type: DataTypes.STRING(config.nameLen),
allowNull: false,
comment: 'module name',
},
count: {
type: DataTypes.BIGINT(20).UNSIGNED,
date: {
type: DataTypes.INTEGER,
allowNull: false,
comment: 'YYYYMM format',
},
d01: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: 'download count',
}
comment: '01 download count',
},
d02: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '02 download count',
},
d03: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '03 download count',
},
d04: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '04 download count',
},
d05: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '05 download count',
},
d06: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '06 download count',
},
d07: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '07 download count',
},
d08: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '08 download count',
},
d09: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '09 download count',
},
d10: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '10 download count',
},
d11: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '11 download count',
},
d12: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '12 download count',
},
d13: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '13 download count',
},
d14: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '14 download count',
},
d15: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '15 download count',
},
d16: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '16 download count',
},
d17: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '17 download count',
},
d18: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '18 download count',
},
d19: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '19 download count',
},
d20: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '20 download count',
},
d21: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '21 download count',
},
d22: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '22 download count',
},
d23: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '23 download count',
},
d24: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '24 download count',
},
d25: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '25 download count',
},
d26: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '26 download count',
},
d27: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '27 download count',
},
d28: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '28 download count',
},
d29: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '29 download count',
},
d30: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '30 download count',
},
d31: {
type: DataTypes.BIGINT(20),
allowNull: false,
defaultValue: 0,
comment: '31 download count',
},
}, {
tableName: 'download_total',
tableName: 'downloads',
comment: 'module download total info',
indexes: [
{
unique: true,
fields: ['date', 'name']
fields: ['name', 'date'],
},
{
fields: ['date'],
}
],
classMethods: {

View File

@@ -1,26 +1,16 @@
/**!
* 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 config = require('../config');
var sequelize = require('../common/sequelize');
function load(name) {
return sequelize.import(path.join(__dirname, name));
}
var _ModuleAbbreviated = config.enableAbbreviatedMetadata ? load('module_abbreviated') : null;
var _PackageReadme = config.enableAbbreviatedMetadata ? load('package_readme') : null;
module.exports = {
sequelize: sequelize,
Module: load('module'),
@@ -36,14 +26,38 @@ module.exports = {
User: load('user'),
Total: load('total'),
DownloadTotal: load('download_total'),
DistFile: load('dist_file'),
DistDir: load('dist_dir'),
Token: load('token'),
query: function* (sql, args) {
return yield this.sequelize.query(sql, null, {raw: true}, args);
var options = { replacements: args };
var data = yield this.sequelize.query(sql, options);
if (/select /i.test(sql)) {
return data[0];
}
return data[1];
},
queryOne: function* (sql, args) {
var rows = yield* this.query(sql, args);
var rows = yield this.query(sql, args);
return rows && rows[0];
}
},
get ModuleAbbreviated() {
if (!config.enableAbbreviatedMetadata) {
return null;
}
if (!_ModuleAbbreviated) {
_ModuleAbbreviated = load('module_abbreviated');
}
return _ModuleAbbreviated;
},
get PackageReadme() {
if (!config.enableAbbreviatedMetadata) {
return null;
}
if (!_PackageReadme) {
_PackageReadme = load('package_readme');
}
return _PackageReadme;
},
};

View File

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

View File

@@ -1,44 +1,27 @@
/**!
* 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`);
`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',
`author` varchar(100) NOT NULL COMMENT 'module author',
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`version` varchar(70) NOT NULL COMMENT 'module version',
`description` longtext COMMENT 'module description',
`package` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'package.json',
`dist_shasum` varchar(100) DEFAULT NULL COMMENT 'module dist SHASUM',
`dist_tarball` varchar(2048) DEFAULT NULL COMMENT 'module dist tarball',
`dist_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'module dist size',
`publish_time` bigint(20) unsigned COMMENT 'module publish time',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_name` (`name`,`version`),
KEY `idx_gmt_modified` (`gmt_modified`),
KEY `idx_publish_time` (`publish_time`),
KEY `idx_author` (`author`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module info';
*/
const config = require('../config');
module.exports = function (sequelize, DataTypes) {
return sequelize.define('Module', {
@@ -48,17 +31,18 @@ module.exports = function (sequelize, DataTypes) {
comment: 'first maintainer name'
},
name: {
type: DataTypes.STRING(100),
type: DataTypes.STRING(config.nameLen),
allowNull: false,
comment: 'module name'
},
version: {
type: DataTypes.STRING(30),
type: DataTypes.STRING(config.versionLen),
allowNull: false,
comment: 'module version'
},
description: {
type: DataTypes.LONGTEXT,
comment: 'module description',
},
package: {
type: DataTypes.LONGTEXT,
@@ -67,19 +51,23 @@ module.exports = function (sequelize, DataTypes) {
dist_shasum: {
type: DataTypes.STRING(100),
allowNull: true,
comment: 'module dist SHASUM',
},
dist_tarball: {
type: DataTypes.STRING(2048),
allowNull: true,
comment: 'module dist tarball',
},
dist_size: {
type: DataTypes.INTEGER.UNSIGNED,
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
comment: 'module dist size',
},
publish_time: {
type: DataTypes.BIGINT(20).UNSIGNED,
type: DataTypes.BIGINT(20),
allowNull: true,
comment: 'module publish time',
}
}, {
tableName: 'module',
@@ -87,16 +75,16 @@ module.exports = function (sequelize, DataTypes) {
indexes: [
{
unique: true,
fields: ['name', 'version']
fields: ['name', 'version'],
},
{
fields: ['gmt_modified']
fields: ['gmt_modified'],
},
{
fields: ['publish_time']
fields: ['publish_time'],
},
{
fields: ['author']
fields: ['author'],
}
],
classMethods: {

View File

@@ -0,0 +1,65 @@
'use strict';
/*
CREATE TABLE IF NOT EXISTS `module_abbreviated` (
`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(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`version` varchar(70) NOT NULL COMMENT 'module version',
`package` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'the abbreviated metadata',
`publish_time` bigint(20) unsigned COMMENT 'the publish time',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_name` (`name`,`version`),
KEY `idx_gmt_modified` (`gmt_modified`),
KEY `idx_publish_time` (`publish_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module abbreviated info';
*/
const config = require('../config');
module.exports = function (sequelize, DataTypes) {
return sequelize.define('ModuleAbbreviated', {
name: {
type: DataTypes.STRING(config.nameLen),
allowNull: false,
comment: 'module name'
},
version: {
type: DataTypes.STRING(config.versionLen),
allowNull: false,
comment: 'module version'
},
package: {
type: DataTypes.LONGTEXT,
comment: 'package.json',
},
publish_time: {
type: DataTypes.BIGINT(20),
allowNull: true,
comment: 'the publish time',
}
}, {
tableName: 'module_abbreviated',
comment: 'module abbreviated info',
indexes: [
{
unique: true,
fields: ['name', 'version'],
},
{
fields: ['gmt_modified'],
},
{
fields: ['publish_time'],
},
],
classMethods: {
findByNameAndVersion: function* (name, version) {
return yield this.find({
where: { name: name, version: version }
});
}
}
});
};

View File

@@ -18,18 +18,19 @@
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`',
`name` varchar(214) 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 `module_deps_name_deps` (`name`,`deps`),
KEY `deps` (`module_deps_deps`)
UNIQUE KEY `uk_name_deps` (`name`,`deps`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module deps';
*/
const config = require('../config');
module.exports = function (sequelize, DataTypes) {
return sequelize.define('ModuleDependency', {
name: {
type: DataTypes.STRING(100),
type: DataTypes.STRING(config.nameLen),
allowNull: false,
comment: 'module name',
},
@@ -46,10 +47,10 @@ module.exports = function (sequelize, DataTypes) {
indexes: [
{
unique: true,
fields: ['name', 'deps']
fields: ['name', 'deps'],
},
{
fields: ['deps']
fields: ['deps'],
}
],
classMethods: {

View File

@@ -19,28 +19,31 @@ 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,
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`description` longtext COMMENT 'module description',
PRIMARY KEY (`id`),
UNIQUE KEY `keyword_module_name` (`keyword`,`name`),
KEY `name` (`name`)
UNIQUE KEY `uk_keyword_module_name` (`keyword`,`name`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module keyword';
*/
const config = require('../config');
module.exports = function (sequelize, DataTypes) {
return sequelize.define('ModuleKeyword', {
keyword: {
type: DataTypes.STRING(100),
allowNull: false,
comment: 'keyword',
},
name: {
type: DataTypes.STRING(100),
type: DataTypes.STRING(config.nameLen),
allowNull: false,
comment: 'module name',
},
description: {
type: DataTypes.LONGTEXT,
allowNull: true,
comment: 'module description',
}
}, {
tableName: 'module_keyword',
@@ -49,10 +52,10 @@ module.exports = function (sequelize, DataTypes) {
indexes: [
{
unique: true,
fields: ['keyword', 'name']
fields: ['keyword', 'name'],
},
{
fields: ['name']
fields: ['name'],
}
],
classMethods: {

View File

@@ -15,17 +15,18 @@
*/
/*
CREATE TABLE IF NOT EXISTS `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',
`username` varchar(100) NOT NULL,
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`log` longtext,
`username` varchar(100) NOT NULL COMMENT 'which username',
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`log` longtext COMMENT 'the raw log',
PRIMARY KEY (`id`),
KEY `module_log_name` (`name`)
KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module sync log';
*/
const config = require('../config');
module.exports = function (sequelize, DataTypes) {
return sequelize.define('ModuleLog', {
@@ -35,19 +36,20 @@ module.exports = function (sequelize, DataTypes) {
comment: 'user name'
},
name: {
type: DataTypes.STRING(100),
type: DataTypes.STRING(config.nameLen),
allowNull: false,
comment: 'module name',
},
log: {
type: DataTypes.LONGTEXT
type: DataTypes.LONGTEXT,
comment: 'the raw log',
}
}, {
tableName: 'module_log',
comment: 'module sync log',
indexes: [
{
fields: ['name']
fields: ['name'],
}
],
classMethods: {

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