Compare commits

...

1274 Commits

Author SHA1 Message Date
Chen Yangjian
31294a819b fix: model init (#1626) 2021-03-02 10:52:32 +08:00
fengmk2
9d9b834488 feat: use leoric for the ORM solution
https://www.cyj.me/leoric/zh/
2021-03-02 09:49:06 +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
fengmk2
b582f73e7d Release 2.0.0-beta5 2014-12-05 19:22:56 +08:00
fengmk2
9b52820a8d hotfix package.html typo. Closes #521 2014-12-05 19:20:00 +08:00
dead_horse
f08c9dd147 update editorconfig 2014-11-25 11:44:18 +08:00
Yiyu He
990a980af1 Merge pull request #517 from CatTail/master
Add editorconfig
2014-11-25 11:41:48 +08:00
zhongchiyu
948d1b652b Add editorconfig 2014-11-25 11:27:47 +08:00
Yiyu He
7bb782179b Merge pull request #514 from cnpm/style-fix
Style fix
2014-11-23 16:01:34 +08:00
fengmk2
1bf7eb7324 fix(web/package): package name to long cause style problem fix
Closes #511
2014-11-23 15:01:39 +08:00
fengmk2
da69df9bb0 fix(css): use github-markdown-css for markdown body
https://github.com/sindresorhus/github-markdown-css
2014-11-23 14:32:19 +08:00
Yiyu He
b475b231df Merge pull request #512 from cnpm/koa-mock
feat(mock): use koa-mock for front end dev
2014-11-22 10:36:09 +08:00
fengmk2
15cb3e66da feat(mock): use koa-mock for front end dev
demo: ![cnpm-pagemock](https://cloud.githubusercontent.com/assets/156269/5144334/808ddb62-71d4-11e4-9af7-506b17b96d18.gif)

Closes #509
2014-11-21 23:19:25 +08:00
fengmk2
9ee511b96c Release 2.0.0-beta4 2014-11-21 22:13:47 +08:00
fengmk2
cad590deb4 Merge pull request #510 from cnpm/hotfix-short
fix(registry): add missing /-/short api
2014-11-21 22:05:35 +08:00
fengmk2
c253994edc fix(registry): add missing /-/short api
Closes #508
2014-11-21 22:03:45 +08:00
fengmk2
2d042af2c6 Merge pull request #507 from cnpm/new-views
New views
2014-11-17 20:00:48 +08:00
dead_horse
ad0dd602e0 sort the array in test 2014-11-17 16:31:23 +08:00
dead_horse
d95221dd7f update test cases 2014-11-17 16:12:47 +08:00
dead_horse
acbf75514d zoom sync link 2014-11-17 15:53:59 +08:00
dead_horse
87ad2002a7 new design for package page 2014-11-17 15:47:16 +08:00
dead_horse
1183d24d26 image max width, fixed #505 2014-11-17 00:22:23 +08:00
Yiyu He
207707feff Merge pull request #503 from cnpm/block-ruby
feat(middleware): block Ruby user-agent
2014-11-12 19:22:23 +08:00
fengmk2
445db415c9 feat(middleware): block Ruby user-agent
Closes #502
2014-11-12 18:37:45 +08:00
fengmk2
b11693c6bc Release 2.0.0-beta3 2014-11-12 17:01:24 +08:00
fengmk2
a28ede9e32 Merge pull request #501 from cnpm/sync-hotfix
fix(sync): should not sync package when maintainers sort change
2014-11-12 16:56:24 +08:00
fengmk2
45fba36a7d fix(sync): should not sync package when maintainers sort change
Closes #500
2014-11-12 16:44:25 +08:00
Yiyu He
4e02b2d211 Merge pull request #498 from cnpm/fix-maintainers
fix(maintainer): fix missing maintainers
2014-11-11 09:22:06 +08:00
fengmk2
e11679f933 update private maintainer only 2014-11-10 23:48:03 +08:00
fengmk2
49d43999e4 fix(package): detect package is private or not
Use `services/common.js` isPrivatePackage(name)
2014-11-10 19:36:24 +08:00
fengmk2
3b71d81e4a fix(maintainer): fix missing maintainers
Closes #497
2014-11-10 18:44:11 +08:00
dead_horse
b92c239325 remove path.exists compat 2014-11-09 20:29:39 +08:00
fengmk2
25c998db33 Release 2.0.0-beta2 2014-11-09 17:44:17 +08:00
fengmk2
fc3ad15c53 fix(sync): add missing syncUpstreamFirst argument 2014-11-09 17:43:44 +08:00
fengmk2
4fda3d0375 Release 2.0.0-beta1 2014-11-07 17:16:00 +08:00
Yiyu He
dc5c3e24e2 Merge pull request #495 from cnpm/refactor-sync
refactor(sync_worker): only sync request need to sync upstream first
2014-11-07 01:37:25 +08:00
fengmk2
140f5b302e refactor(sync_worker): only sync request need to sync upstream first 2014-11-07 00:56:34 +08:00
fengmk2
aa9d6479de fix(sync_worker): make sure end event will emit 2014-11-07 00:42:01 +08:00
fengmk2
651d4b207e fix: mv readme.md script to public/js/readme.js 2014-11-06 23:44:24 +08:00
fengmk2
c8d66f6d78 fix(sync): hotfix co uncaughtException
Closes #494
2014-11-06 23:25:14 +08:00
Yiyu He
c02e66fea5 Merge pull request #493 from cnpm/sync-python-dist
feat(sync): sync python dist
2014-11-06 18:01:14 +08:00
fengmk2
ff51d7febf feat(sync): sync python dist
https://www.python.org/ftp/python/

Closes #492
2014-11-06 17:40:38 +08:00
dead_horse
2dc92530f5 hot fix remarkable 2014-11-06 16:46:54 +08:00
fengmk2
d835526eda Merge pull request #491 from cnpm/refactor-maintainers
refactor models/_module_maintainer_class_methods.js
2014-11-03 00:45:11 +08:00
dead_horse
6f29f4cc3b pin autod@1 2014-11-03 00:24:57 +08:00
dead_horse
a5baac4f7f remove useless comment 2014-11-03 00:19:29 +08:00
dead_horse
8f1f919d2e refactor models/_module_maintainer_class_methods.js 2014-11-03 00:06:49 +08:00
fengmk2
8e2f0052dc Release 2.0.0-beta0 2014-11-02 23:50:41 +08:00
Yiyu He
6e77a171fd Merge pull request #488 from cnpm/limit-log-size
fix(module_log): limit module sync log size to 1MB
2014-11-02 11:48:03 +08:00
fengmk2
3b08a7fd21 Merge pull request #490 from cnpm/issue482-remarkable
ungrade koa-markdown to use remarkable, close #482
2014-11-02 11:42:01 +08:00
dead_horse
e3f4d8d158 ungrade koa-markdown to use remarkable, close #482 2014-11-02 02:55:14 +08:00
fengmk2
5b3d92ee43 fix(module_log): limit module sync log size to 1MB
If log size bigger than 1MB, it will be auto slice to 0.5MB
2014-11-01 22:32:46 +08:00
fengmk2
c1e35cabe3 refactor(config): remove adaptScope config key 2014-11-01 22:03:39 +08:00
fengmk2
63d604d9bb chore(Makefile): $ make install-production 2014-10-31 16:09:00 +08:00
fengmk2
b402f04fc9 fix(sequelize): show warnning message when using old config.js 2014-10-31 16:05:07 +08:00
fengmk2
a4920fdcee docs(readme): Migrating from 1.x to 2.x
Add new wiki: https://github.com/cnpm/cnpmjs.org/wiki/Migrating-from-1.x-to-2.x
2014-10-31 15:22:22 +08:00
Yiyu He
798f357132 Merge pull request #484 from cnpm/sequelize-merge
Sequelize instead of raw SQL
2014-10-31 14:31:55 +08:00
fengmk2
9ce8482a13 feat(sync): add min sync interval time detect
Forbidden some guy write `config.syncInterval = 1` to let sync too fast.

Closes #486
2014-10-31 14:16:16 +08:00
fengmk2
23067fa1a2 refactor(dispatch): remove unused codes 2014-10-31 14:11:44 +08:00
fengmk2
abdbf69f25 remove duplicate db_engine 2014-10-30 16:01:59 +08:00
fengmk2
a4d7331f52 fix blank line 2014-10-30 15:42:42 +08:00
fengmk2
fac45324b9 fix(sync): make sure save log in sequeue 2014-10-30 15:36:58 +08:00
fengmk2
98c750df1a show db engine on registry:/ 2014-10-30 14:30:57 +08:00
fengmk2
22335db1de docs(readme): change dev on local database deps 2014-10-30 14:15:35 +08:00
fengmk2
47ad267df5 fix tests 2014-10-30 13:42:51 +08:00
fengmk2
49b27d562d merge sequelize 2014-10-30 13:38:01 +08:00
fengmk2
f49ce17457 refactor(sync): fix sync/*.js tests 2014-10-30 12:11:15 +08:00
fengmk2
128383d032 refactor(controllers): controllers all change to ORM 2014-10-29 23:43:00 +08:00
fengmk2
f47615501c web/package/*.js done 2014-10-29 18:11:40 +08:00
fengmk2
adae52cef3 /package/:name refactor done 2014-10-29 17:35:56 +08:00
fengmk2
30407dee07 merge download.js to total.js 2014-10-29 16:35:10 +08:00
fengmk2
1ba6f08f4b rename download filename 2014-10-29 11:25:58 +08:00
fengmk2
083104e2e1 registry controller done 2014-10-29 11:19:58 +08:00
fengmk2
12b09ab559 fix use_package.js 2014-10-29 02:04:39 +08:00
fengmk2
cf910e1d8c add list package names since startkey 2014-10-29 00:14:41 +08:00
fengmk2
eda52b0a3e list all package names 2014-10-28 19:25:36 +08:00
fengmk2
cd2fd4a0b5 fix test 2014-10-28 19:15:03 +08:00
fengmk2
8cfa7a4d8b improve coverage 2014-10-28 18:56:36 +08:00
fengmk2
716efab4ea add remove package test cases 2014-10-28 18:44:50 +08:00
fengmk2
a0ec1c8521 refactor remove package 2014-10-28 14:59:34 +08:00
fengmk2
be1a966ce1 add package publish and add tag test cases 2014-10-28 14:14:20 +08:00
fengmk2
c5b5064e40 module controllers 拆分到 controllers/package/*.js 2014-10-28 00:14:27 +08:00
Yiyu He
de7bef05c2 fix badge 2014-10-25 10:18:51 +08:00
dead_horse
c03be9c69d add gitter 2014-10-25 10:13:45 +08:00
fengmk2
dd092b356d user data all from services/user.js 2014-10-20 01:56:47 +08:00
fengmk2
d3e93f3990 remove all proxy/*.js 2014-10-20 00:54:33 +08:00
fengmk2
07accd62e4 mv proxy/module_unpublished to models/module_unpublished 2014-10-20 00:31:46 +08:00
fengmk2
8c280bd789 refactor registry download request 2014-10-19 23:49:41 +08:00
fengmk2
3d0674fea3 mv proxy/module_maintainer to models/module_maintainer 2014-10-19 23:29:53 +08:00
fengmk2
393bd42938 mv proxy/dist.js to services/dist.js 2014-10-19 01:46:25 +08:00
fengmk2
1ac1cf82d8 mv proxy/module_log to services/module_log 2014-10-19 00:58:17 +08:00
fengmk2
206d595f02 refator module dependency 2014-10-19 00:36:29 +08:00
fengmk2
aa135d2a30 mv proxy/module.js into services/package.js 2014-10-16 00:01:55 +08:00
dead_horse
d2949c9450 Release 1.7.1 2014-10-15 02:43:01 +08:00
dead_horse
7e583b2edf fix typo in sync popular, fix #477 2014-10-15 02:42:24 +08:00
dead_horse
9b13c184fe Release 1.7.0 2014-10-15 02:26:35 +08:00
Yiyu He
1830301ae6 Merge pull request #475 from KidkArolis/configurable-short-registry-url
If sourceNpmRegistry is cnpm - use it in getShort
2014-10-15 02:22:56 +08:00
Karolis Narkevicius
7774a5c6d9 If sourceNpmRegistry is cnpm - use it in getShort
Only fallback to r.cnpmjs.org in case sourceNpmRegistry is not cnpm
2014-10-14 19:21:11 +01:00
fengmk2
05c6d7b4f6 Merge pull request #472 from cnpm/issue468-upon
add sync popular modules, close #468
2014-10-13 11:54:39 +08:00
fengmk2
d30260f301 --build-from-source 2014-10-13 03:29:49 +08:00
fengmk2
c8990083a7 mv proxy/total.js to services/total.js 2014-10-13 03:17:44 +08:00
fengmk2
ef246c2ec4 controllers/total.js done 2014-10-13 02:10:38 +08:00
dead_horse
2a8bd139d0 fix test label 2014-10-13 00:48:37 +08:00
dead_horse
ea322c3310 add sync popular modules, close #468 2014-10-13 00:42:06 +08:00
fengmk2
48d8cb81fc init database before run test 2014-10-12 22:09:19 +08:00
fengmk2
dd9f4a7f2e change proxy/user.js => models/user.js 2014-10-12 21:29:32 +08:00
fengmk2
56eb1b1640 use sequelize to connect database 2014-10-12 14:07:22 +08:00
fengmk2
4b7a191b73 Change version to 2.0.0 2014-10-12 13:03:06 +08:00
fengmk2
d48cf497cf add sendmail test script 2014-10-09 10:58:24 +08:00
fengmk2
abaac6089a Release 1.6.1 2014-10-09 01:15:09 +08:00
fengmk2
e8ba8eadb3 Merge pull request #466 from cnpm/sync-npm-package-maintainers
save npm original package maintainers to npm_module_maintainer table.
2014-10-09 00:46:29 +08:00
fengmk2
20093741eb make test on travis faster 2014-10-09 00:33:50 +08:00
fengmk2
51ddf2cf02 ensure not sync user also has his own package names 2014-10-09 00:24:14 +08:00
fengmk2
26efecd2fc add v1.6.x-upgrade sql 2014-10-09 00:01:50 +08:00
fengmk2
678c85102e save npm original package maintainers to npm_module_maintainer table. fixed #464 2014-10-09 00:01:14 +08:00
fengmk2
439b6663f3 use simple 404 2014-10-08 18:40:23 +08:00
fengmk2
3069179718 fix possible xss on 404 page 2014-10-08 18:29:13 +08:00
fengmk2
f2613edcd5 Release 1.6.0 2014-10-08 11:15:44 +08:00
dead_horse
6e9ceaf844 Merge pull request #463 from cnpm/by-user-api
list user all packages api. fixed #462
2014-10-08 01:06:14 +08:00
fengmk2
188dab4ed8 list user all packages api. fixed #462 2014-10-08 00:53:20 +08:00
dead_horse
5d0d742e85 Merge pull request #461 from cnpm/node-dev
add node-dev: $ make dev
2014-10-04 01:56:15 +08:00
fengmk2
92a828c04d update readme 2014-10-04 00:33:27 +08:00
fengmk2
3c804758e7 add node-dev: $ make dev 2014-10-04 00:26:08 +08:00
fengmk2
683b2e3640 Merge pull request #459 from cnpm/sync
always start sync worker
2014-10-03 21:11:39 +08:00
dead_horse
35d445cd11 always start sync worker 2014-10-03 02:06:44 +08:00
fengmk2
45a9e0ff97 Merge pull request #458 from cnpm/nodemailer
update node mailer
2014-10-02 09:14:51 +08:00
dead_horse
dda2f0785c update node mailer 2014-10-02 01:58:43 +08:00
dead_horse
ce02d2f48e update autod 2014-10-02 01:10:03 +08:00
fengmk2
c36523da17 Release 1.5.5 2014-09-25 00:00:46 +08:00
fengmk2
93f33a98cf fix sync in web 2014-09-24 23:44:03 +08:00
dead_horse
c8372f06a8 Merge pull request #454 from cnpm/sync-upstream-on-frist-package
sync upstream only the first package. ...
2014-09-24 23:38:25 +08:00
fengmk2
234e8bf27b sync upstream only the first package. make sync devDependencies optionsal, default is false 2014-09-24 23:34:35 +08:00
fengmk2
dfe56b6222 Merge pull request #453 from cnpm/config
add some comment, default sourceNpmRegistryIsCNpm to true
2014-09-24 23:14:13 +08:00
dead_horse
8548104ee6 add some comment, default sourceNpmRegistryIsCNpm to true 2014-09-24 23:10:13 +08:00
fengmk2
ceeb617f0d Release 1.5.4 2014-09-24 23:04:31 +08:00
fengmk2
91ee61c997 format sync log 2014-09-24 23:04:15 +08:00
fengmk2
1d94ba4e11 Release 1.5.3 2014-09-24 22:23:32 +08:00
dead_horse
5b09d974fb Merge pull request #452 from cnpm/sync-upstream-package
support sync upstream first. fixed #451
2014-09-24 19:54:36 +08:00
fengmk2
763224bbbb support sync upstream first. fixed #451 2014-09-24 19:43:51 +08:00
fengmk2
7b6c757b83 Release 1.5.2 2014-09-24 18:22:36 +08:00
fengmk2
fd915e0c5d support im url on user profile page 2014-09-24 18:21:31 +08:00
dead_horse
fb39eb8c2a Release 1.5.1 2014-09-23 17:15:17 +08:00
dead_horse
f139cccad0 search support case insensitive, close #450 2014-09-23 17:14:03 +08:00
fengmk2
4bb2e6a5d8 Merge pull request #449 from cnpm/sync-in-web
add config._syncInWeb, close #448
2014-09-23 09:20:06 +08:00
dead_horse
4d3c257474 add config._syncInWeb, close #448 2014-09-23 02:02:56 +08:00
dead_horse
b990bd9cf1 Merge pull request #445 from cnpm/show-maintainers-when-publish-fail
show maintainers when publish 403. fixed #430
2014-09-16 01:13:21 +08:00
fengmk2
917b407d1e show maintainers when publish 403. fixed #430 2014-09-16 01:05:40 +08:00
dead_horse
774fd44b12 Merge pull request #444 from cnpm/no-attachment-for-html
no attachment for html
2014-09-15 17:12:45 +08:00
fengmk2
a6b67f5453 no attachment for html 2014-09-15 17:10:49 +08:00
fengmk2
1e56c77deb Release 1.5.0 2014-09-15 16:50:20 +08:00
dead_horse
e1e6b6b674 Merge pull request #441 from cnpm/dist-sync-document
dist sync document too. fixed #420
2014-09-14 18:30:27 +08:00
fengmk2
615d16165c dist sync document too. fixed #420 2014-09-13 00:33:20 +08:00
fengmk2
8986503005 Release 1.4.4 2014-09-12 15:37:57 +08:00
fengmk2
4b5a49b626 badge version support 1.0.0-beta1. fixed #440 2014-09-12 11:54:58 +08:00
fengmk2
fce0876295 Release 1.4.3 2014-09-09 12:06:53 +08:00
fengmk2
9df81764a9 alias /:name/-/:file to /:name/download/:file. fixed #439 2014-09-09 12:06:05 +08:00
dead_horse
4801f15768 Release 1.4.2 2014-09-03 13:12:53 +08:00
dead_horse
d24422fdba change default source registry to taobao's registry 2014-09-03 13:11:55 +08:00
dead_horse
835f898eb3 Merge pull request #435 from cnpm/bluebird
add bluebird
2014-09-03 10:17:02 +08:00
fengmk2
2a3a0436e7 add bluebird 2014-09-03 10:12:05 +08:00
dead_horse
0a782ea014 bump fs-cnpm 2014-09-03 09:38:54 +08:00
dead_horse
3813827d77 Merge pull request #434 from cnpm/agent-stat
show agent sockets stat. fixed #433
2014-09-03 09:35:51 +08:00
fengmk2
264544c780 show agent sockets stat. fixed #433 2014-09-03 00:41:04 +08:00
dead_horse
416afb2e9a update readme 2014-09-02 20:51:25 +08:00
dead_horse
fe15eb78e9 remove pic in readme 2014-09-02 20:50:47 +08:00
dead_horse
64d2facef3 Release 1.4.1 2014-08-20 23:19:52 +08:00
dead_horse
d677e804b5 Merge branch 'master' of github.com:cnpm/cnpmjs.org 2014-08-20 23:18:35 +08:00
dead_horse
f4dae42589 fix login error status 2014-08-20 23:18:24 +08:00
fengmk2
71452f692e Release 1.4.0 2014-08-20 10:01:48 +08:00
dead_horse
c24526d7ff Merge pull request #429 from cnpm/badge
Badge version image
2014-08-20 09:56:48 +08:00
fengmk2
43dcd4b4f1 update readme for badge url desc 2014-08-20 09:53:37 +08:00
fengmk2
ff90c0f238 different version, different color badge 2014-08-20 09:47:06 +08:00
fengmk2
f85752e4bc add badge on package page 2014-08-20 00:25:26 +08:00
fengmk2
4b20c13a4f add version badge. fixed #427 2014-08-20 00:07:53 +08:00
dead_horse
ef61605e0c add download and node version badge 2014-08-18 21:32:52 +08:00
dead_horse
a5291d95dd Release 1.3.2 2014-08-18 11:45:25 +08:00
dead_horse
9de10e791c remove unused eventproxy 2014-08-18 11:27:25 +08:00
dead_horse
1162ed95b0 add custom config in tools/sync_not_exist.js 2014-08-18 11:25:09 +08:00
dead_horse
6cdbc0b058 Release 1.3.1 2014-08-18 10:15:49 +08:00
dead_horse
dc7239637d add sync not exist tools, close #424 2014-08-18 10:14:38 +08:00
dead_horse
b735c9c15c use gittip instand of alipay. close #425 2014-08-17 22:21:59 +08:00
dead_horse
417e19a330 update registry api doc 2014-08-16 16:01:06 +08:00
dead_horse
40ab0929e1 Release 1.3.0 2014-08-11 11:46:58 +08:00
dead_horse
6dabe60839 ignore config/config.js 2014-08-11 11:46:29 +08:00
fengmk2
2680512c71 Merge pull request #421 from cnpm/qn-cnpm
use simple fs storage in develop env
2014-08-10 08:59:40 +08:00
dead_horse
f444566a23 fix test case 2014-08-10 01:57:58 +08:00
dead_horse
99cb110ee2 use fs-cnpm 2014-08-10 01:27:08 +08:00
dead_horse
536704705c fix test 2014-08-09 21:32:36 +08:00
dead_horse
d919a81dae use qn-cnpm 2014-08-09 21:17:59 +08:00
dead_horse
fe8100ea30 bump cfork 2014-08-09 20:16:56 +08:00
dead_horse
764c32646e Release 1.2.2 2014-08-08 10:47:50 +08:00
dead_horse
dea16e72de bump koa 2014-08-08 10:45:50 +08:00
fengmk2
7f951a6c5d Release 1.2.1 2014-08-07 18:19:08 +08:00
fengmk2
a1f2bc87b7 Merge pull request #418 from cnpm/undeprecate
deprecated bug fix and support undeprecate
2014-08-07 18:18:31 +08:00
fengmk2
706a5e053b deprecated bug fix and support undeprecate 2014-08-07 18:06:32 +08:00
fengmk2
eddc1a1bb9 Release 1.2.0 2014-08-07 17:27:39 +08:00
dead_horse
6e5abe44b9 Merge pull request #417 from cnpm/deprecate-api
Deprecate API
2014-08-07 17:20:30 +08:00
fengmk2
e6484e7e7b show deprecated message 2014-08-07 17:18:44 +08:00
fengmk2
7a88fa49d2 Sync deprecated field if it missing 2014-08-07 16:48:41 +08:00
fengmk2
82189bd393 Support $ cnpm deprecate [pkgname]@[version] "message". fixed #415 2014-08-07 16:24:35 +08:00
fengmk2
7834f44901 Release 1.1.0 2014-08-07 10:18:39 +08:00
dead_horse
463cf44133 Merge pull request #414 from cnpm/add-maintainers-bugfix
Add user to maintainers when publish. fixed #395
2014-08-07 09:21:17 +08:00
fengmk2
e6bb4b76c2 Add user to maintainers when publish. fixed #395
Also remove the maintainers when package unpublish.
2014-08-07 00:32:10 +08:00
fengmk2
b86d8eb110 List all npm registry api. close #413 2014-08-06 00:49:28 +08:00
dead_horse
070e72b571 Merge pull request #412 from cnpm/limit-list-since
limit list since
2014-08-05 09:24:16 +08:00
fengmk2
6e6f87c147 limit list since 2014-08-05 01:51:19 +08:00
dead_horse
bc9306eda7 Merge pull request #411 from cnpm/deps-change
change deps by "~"
2014-08-04 23:30:02 +08:00
fengmk2
66ea71756c change deps by "~" 2014-08-04 23:25:12 +08:00
dead_horse
0a1fff70cc Merge pull request #409 from cnpm/cfork
use cfork to make sure worker fork and restart
2014-08-04 21:08:05 +08:00
fengmk2
71da60c16f use cfork to make sure worker fork and restart 2014-08-04 18:53:55 +08:00
dead_horse
b5d585988f Merge pull request #405 from cnpm/master-uncaughtException
handle master uncaughtException. fixed #403
2014-08-04 12:27:41 +08:00
fengmk2
758d1d4320 handle master uncaughtException. fixed #403 2014-08-04 12:26:26 +08:00
fengmk2
7b77f09264 Release 1.0.6 2014-08-02 02:13:44 +08:00
fengmk2
7731b44ab4 WTF moment@2.8.0 missing 2014-08-02 02:13:24 +08:00
fengmk2
4bc6998040 Release 1.0.5 2014-08-02 02:04:36 +08:00
fengmk2
0d28fbde54 fix remove tar test cases 2014-08-02 02:03:59 +08:00
fengmk2
660035c1d5 Merge pull request #401 from cnpm/hotfix-unpublish-version
unpublish pkg@version bug hotfix. fixed #400
2014-08-02 01:57:19 +08:00
fengmk2
f21e5a1c07 unpublish pkg@version bug hotfix. fixed #400 2014-08-02 01:56:32 +08:00
fengmk2
4d20d91965 Release 1.0.4 2014-08-01 18:40:45 +08:00
fengmk2
a3e33850fc hotfix #399 use not exists 2014-08-01 18:39:53 +08:00
fengmk2
d888ef3297 Release 1.0.3 2014-08-01 13:31:54 +08:00
fengmk2
ce4ec7e6e9 Merge pull request #397 from cnpm/list-users-packages
add maintaining packages in user page
2014-08-01 13:19:30 +08:00
dead_horse
fe319b06ba Release 1.0.2 2014-08-01 12:05:54 +08:00
dead_horse
d829600ed0 ~_~ fix response message 2014-08-01 12:05:25 +08:00
dead_horse
4dd59cb300 Release 1.0.1 2014-08-01 12:00:33 +08:00
dead_horse
5ff25474c0 Merge pull request #398 from cnpm/fix-auth
hot fix auth error
2014-08-01 11:58:54 +08:00
dead_horse
6d49a859c6 hot fix auth error 2014-08-01 11:58:23 +08:00
dead_horse
094178c3ca add maintaining packages in user page 2014-08-01 10:47:47 +08:00
fengmk2
63a57b906e Release 1.0.0 2014-08-01 09:26:04 +08:00
fengmk2
a09e6b142d Merge pull request #394 from cnpm/list-pravites
add private package list
2014-08-01 08:55:11 +08:00
dead_horse
bca9341e5d add private package list 2014-08-01 01:43:13 +08:00
fengmk2
71b0662d49 Release 0.9.2 2014-07-30 23:15:11 +08:00
fengmk2
b894c0fa2b hotfix save custom user bug 2014-07-30 23:14:53 +08:00
fengmk2
4127fa1f07 Release 0.9.1 2014-07-30 17:20:17 +08:00
dead_horse
5ee2a6e8fd Merge pull request #390 from cnpm/handle-user-service-auth-throw-error
Handle user service auth throw custom error message
2014-07-30 10:16:24 +08:00
fengmk2
8baa34145b Handle user service auth throw custom error message 2014-07-30 10:05:55 +08:00
fengmk2
4acb819356 Merge pull request #389 from cnpm/private-white-list
Private white list
2014-07-30 08:21:43 +08:00
dead_horse
2a84e39aee add test for config private packages 2014-07-30 00:52:44 +08:00
dead_horse
2019d72e36 add config.privatePackages 2014-07-29 23:55:42 +08:00
dead_horse
b9fa7e7f98 add more comments in config/index.js 2014-07-29 23:31:22 +08:00
fengmk2
694795e451 Release 0.9.0 2014-07-29 10:24:56 +08:00
dead_horse
fae67880a5 Merge pull request #388 from cnpm/user-service
Custom user service. fixed #385
2014-07-29 10:17:08 +08:00
fengmk2
0929748cbd listMaintainers only return name and email 2014-07-29 10:06:28 +08:00
fengmk2
a87945c6a0 remove sessions 2014-07-29 01:55:18 +08:00
fengmk2
d7345dcc76 scopes init mv to services/user.js 2014-07-29 01:51:32 +08:00
fengmk2
1a9ea8c6cf show user more profile 2014-07-29 01:42:02 +08:00
fengmk2
8f0ada7bf8 registry show user support custom user service 2014-07-29 00:54:18 +08:00
fengmk2
ddd9c9557d support custom user service for user auth 2014-07-29 00:11:34 +08:00
fengmk2
9b2a846865 remove session middleware 2014-07-28 09:17:42 +08:00
fengmk2
b1b238e20c add DefaultUserService 2014-07-28 09:17:42 +08:00
fengmk2
26031a49d2 Merge pull request #383 from cnpm/issue382-limit-scope
Issue382 limit scope
2014-07-28 09:15:39 +08:00
dead_horse
8bfc8ce71d check scopes in module.getAdapt 2014-07-27 23:32:58 +08:00
dead_horse
dffae785ca test public mode, fix some logic, close #382 2014-07-27 23:28:38 +08:00
dead_horse
078d7ddf3d fix sync scope test 2014-07-27 23:28:38 +08:00
dead_horse
8ec082db89 move scope.js into publishable.js, add forcePublishWithScope 2014-07-27 23:28:38 +08:00
dead_horse
040b727878 config.scopes not exist, means do not support scope 2014-07-27 23:28:38 +08:00
dead_horse
02e1ba63d6 fix scope test cases 2014-07-25 09:39:33 +08:00
dead_horse
a16883618e add test, fix middleware 2014-07-25 00:23:26 +08:00
dead_horse
7bd1c85d4e fix adapt scope 2014-07-25 00:12:01 +08:00
dead_horse
336e6f0da8 fix debug info 2014-07-24 23:55:07 +08:00
dead_horse
d43266670e add assert scope middleware 2014-07-24 23:54:53 +08:00
fengmk2
8fdbbf0b04 Release 0.8.7 2014-07-24 00:05:47 +08:00
fengmk2
b62a3b78fb fix unpublished info missing maintainers cause TypeError 2014-07-24 00:05:28 +08:00
fengmk2
d6cf093844 Release 0.8.6 2014-07-23 13:07:33 +08:00
fengmk2
081f0cb4f8 show unpublished info on web package page. fixes #381 2014-07-23 13:06:20 +08:00
fengmk2
0bf18639b6 Release 0.8.5 2014-07-22 23:38:58 +08:00
dead_horse
cbe7a165b0 Merge pull request #380 from cnpm/private-package-default-scoped
Only private package support default scoped. fixed #378
2014-07-22 23:14:31 +08:00
fengmk2
7cadfb4007 Only private package support default scoped. fixed #378 2014-07-22 21:55:45 +08:00
fengmk2
ec51fcf70c Release 0.8.4 2014-07-22 13:32:47 +08:00
fengmk2
565a7d3cfe Merge pull request #379 from cnpm/get-adapt
adapt default scpoe in /@:scope/:name/:version
2014-07-22 12:51:53 +08:00
dead_horse
4ac857fba2 adapt default scpoe in /@:scope/:name/:version 2014-07-22 11:42:18 +08:00
fengmk2
af7c952d2f Release 0.8.3 2014-07-22 10:14:12 +08:00
fengmk2
6705172d4b hot fix download 2014-07-22 10:13:55 +08:00
fengmk2
c0295ecc77 Release 0.8.2 2014-07-22 01:00:38 +08:00
fengmk2
aaf7f04d08 fix default scope detect 2014-07-22 01:00:22 +08:00
fengmk2
beceac10dc Release 0.8.1 2014-07-21 23:54:45 +08:00
dead_horse
fad58273b6 Merge pull request #377 from cnpm/default-scope
support default @org. close #376
2014-07-21 17:16:21 +08:00
fengmk2
e0361dc1f2 add more test cases 2014-07-21 16:18:28 +08:00
fengmk2
d8c337acc5 support default @org. close #376 2014-07-21 16:13:43 +08:00
dead_horse
9d7683bd61 hotfix redis init error 2014-07-21 14:14:52 +08:00
fengmk2
994b2e47aa Release 0.8.0 2014-07-21 13:58:24 +08:00
dead_horse
b14faad3fb Merge pull request #375 from cnpm/scope-package
support scoped package
2014-07-21 13:56:13 +08:00
fengmk2
9163f793a3 update readme show scoped packages 2014-07-21 13:53:33 +08:00
fengmk2
1c0d4fdcdc support "scoped" packages. close #352 2014-07-21 13:51:43 +08:00
dead_horse
20fce9f55f Merge pull request #373 from cnpm/stop-old-publish-flow
Stop old publish flow
2014-07-19 01:21:22 +08:00
fengmk2
6b73a7762f use safe jsonp 2014-07-19 01:16:30 +08:00
fengmk2
fd34e0512c Stop support old publish flow. fix #368 2014-07-19 01:11:33 +08:00
fengmk2
a84715167e Merge pull request #372 from cnpm/sql
update SQLs
2014-07-13 07:07:59 +08:00
dead_horse
db1cb0d0f8 update SQLs 2014-07-12 23:20:55 +08:00
dead_horse
dc137f0821 bump redis 2014-07-12 14:57:23 +08:00
fengmk2
76d270fc77 Merge pull request #371 from cnpm/update-logger
use sync_info and sync_error categories
2014-07-11 12:06:28 +08:00
dead_horse
b5190b56e8 use sync_info and sync_error categories 2014-07-11 10:03:41 +08:00
fengmk2
d2d238db59 add categories to loggers. fix #370 2014-07-11 09:07:48 +08:00
fengmk2
4c02d84fec fix get latest tag always not exists bug 2014-07-11 01:04:52 +08:00
dead_horse
74585f0b8a Merge pull request #369 from cnpm/publish-with-tag
support `npm publish --tag beta`. fix #366
2014-07-10 23:47:48 +08:00
fengmk2
a6ac632e73 support npm publish --tag beta. fix #366 2014-07-10 23:34:25 +08:00
fengmk2
0b59547ac1 Merge pull request #365 from cnpm/logger
use mini-logger and error-formater
2014-07-09 11:30:48 +08:00
dead_horse
b5929c94e0 use mini-logger and error-formater 2014-07-09 11:23:20 +08:00
fengmk2
4c96682603 Release 0.7.0 2014-07-07 12:11:12 +08:00
dead_horse
78af0fc15b Merge pull request #364 from cnpm/maintainer
Maintainer logic improve
2014-07-07 11:42:53 +08:00
fengmk2
ce381618a0 use module_maintainers on GET /pakcage/:name page 2014-07-07 10:25:06 +08:00
fengmk2
87486c484f use new module_maintainers on GET /:name 2014-07-07 10:23:53 +08:00
fengmk2
4608712e05 admin user should never publish to other user's packages. fix #363
but admin user need have the permission to delete other's packages.
2014-07-07 01:41:03 +08:00
fengmk2
3c6576bcab Add a new table for module-maintainers.
* fix #362 update maintianers change the lastmodified. remove version and change last modified.
* #363 change isMaintainer detect logic.
2014-07-07 00:56:44 +08:00
fengmk2
ba1986b931 gravatar use https 2014-07-02 00:09:56 +08:00
fengmk2
5efa508376 support https 2014-07-01 23:26:48 +08:00
dead_horse
38617d4572 bump dependencies 2014-06-30 17:31:04 +08:00
fengmk2
a9a04b370d Release 0.6.1 2014-06-18 12:27:47 +08:00
fengmk2
ec1d924dc1 hot fix removeTagsByNames() 2014-06-17 10:41:41 +08:00
fengmk2
234d1ec4d6 fix _rev not exists 2014-06-16 15:52:56 +08:00
dead_horse
b8f4958d8f Merge pull request #357 from cnpm/fix-sync-request
sync unpublished on GET /sync/:name
2014-06-16 15:23:22 +08:00
fengmk2
e56a21e890 sync unpublished on GET /sync/:name 2014-06-16 15:21:53 +08:00
fengmk2
e3434a58c5 Release 0.6.0 2014-06-16 15:06:17 +08:00
dead_horse
cd9d38aadb Merge pull request #356 from cnpm/unpublished
sync unpublished info. close #353
2014-06-16 13:50:05 +08:00
fengmk2
0ff22ccc60 sync unpublished info. close #353 2014-06-16 10:54:02 +08:00
dead_horse
e4cfb13383 Merge pull request #354 from cnpm/delete-not-exists-versions
Delete not exists versions on sync worker. #353
2014-06-16 09:39:58 +08:00
fengmk2
23ad3a29b4 Delete not exists versions on sync worker. #353 2014-06-15 00:57:18 +08:00
dead_horse
f11b4e9954 Release 0.5.3 2014-06-13 18:08:12 +08:00
dead_horse
6e775eed47 fix sync response 204 2014-06-13 18:05:09 +08:00
dead_horse
ca6841c042 add links in History.md 2014-06-08 00:59:54 +08:00
dead_horse
652b900fc4 bump koa 2014-06-08 00:59:22 +08:00
dead_horse
43379cc66d fix test-cov 2014-06-04 17:37:19 +08:00
dead_horse
8f9b3db029 bump koa and should 2014-06-04 17:29:52 +08:00
fengmk2
884b75f6a3 Release 0.5.2 2014-06-04 15:41:01 +08:00
dead_horse
a958ee40c4 Merge pull request #350 from cnpm/hotfix
sync hotfix
2014-06-04 15:39:05 +08:00
fengmk2
1a01909750 sync hotfix 2014-06-04 15:35:50 +08:00
dead_horse
6901a5c994 Merge pull request #349 from cnpm/dist-sync-phantomjs
sync phantomjs downloads pkg. close #348
2014-06-04 15:12:31 +08:00
fengmk2
33b13dc30e sync phantomjs downloads pkg. close #348 2014-06-04 14:35:20 +08:00
fengmk2
73d9d2c3d0 Merge pull request #347 from cnpm/issue346-restart-syncer
add restart, fixed #346
2014-06-04 12:39:34 +08:00
dead_horse
8b955adb42 add restart, fixed #346 2014-06-04 10:57:50 +08:00
fengmk2
1f219cbd1b Release 0.5.1 2014-05-28 09:18:15 +08:00
dead_horse
17025d42b4 Merge pull request #344 from cnpm/list-since-from-tag
fix attack on /-/all/since?stale=update_after&startkey=2 close #336
2014-05-28 09:09:43 +08:00
fengmk2
922f26b052 fix attack on /-/all/since?stale=update_after&startkey=2 close #336
Also add modified_time key to tag
2014-05-28 00:19:26 +08:00
dead_horse
9371685188 bump thunkify-wrap 2014-05-26 21:44:51 +08:00
dead_horse
ad0b66c7e5 bump koa-middlewares 2014-05-25 01:03:19 +08:00
dead_horse
5588880ec0 remove outputError 2014-05-25 00:44:26 +08:00
dead_horse
9ad72cbdcb bump dependencies 2014-05-25 00:43:16 +08:00
dead_horse
4d033471fc use svg badge 2014-05-25 00:36:58 +08:00
fengmk2
590b7f7eee Merge pull request #340 from cnpm/404-page
add package/notfound page
2014-05-19 19:26:34 +08:00
dead_horse
0ce817f9f3 add package/notfound page 2014-05-19 19:16:15 +08:00
fengmk2
59af2bfd56 add dist mirror link to home page 2014-05-13 09:37:46 +08:00
fengmk2
a83f6bb183 fix sync listdiff and add more test cases 2014-05-13 09:28:54 +08:00
fengmk2
b6f1531743 Release 0.5.0 2014-05-13 08:32:51 +08:00
dead_horse
42d0b538ca Merge pull request #337 from cnpm/dist-sync
Dist sync
2014-05-12 23:48:52 +08:00
fengmk2
a240eb9922 filter /nightlies/* 2014-05-12 17:32:06 +08:00
fengmk2
6853b73fb2 use koa setter instead of set() 2014-05-12 17:09:51 +08:00
fengmk2
a887acec05 update co-urllib 2014-05-12 10:19:19 +08:00
fengmk2
1d3f0f0ad3 add more info on error email 2014-05-12 10:03:01 +08:00
fengmk2
20e04065df add sync dist to sync/index.js 2014-05-12 07:38:48 +08:00
fengmk2
1cee298bc3 show dist page 2014-05-11 20:56:40 +08:00
fengmk2
854bc6c9d8 sync dist file and save it to database 2014-05-10 18:59:13 +08:00
fengmk2
b70c1c421a disable gzip before #335 has fix 2014-05-04 20:05:55 +08:00
dead_horse
b1b6172892 Release 0.4.3 2014-04-18 16:15:50 +08:00
fengmk2
e5a77a4368 Merge pull request #334 from cnpm/fix-permission
add permission check to /:name/:tag
2014-04-18 14:34:27 +08:00
dead_horse
b74cccd342 add permission check to /:name/:tag 2014-04-18 14:29:42 +08:00
fengmk2
6aae538f49 Merge pull request #333 from cnpm/issue332-tag
add put /:name/:tag, close #332
2014-04-18 14:12:56 +08:00
dead_horse
74c8b25374 fix space 2014-04-18 14:07:49 +08:00
dead_horse
0cec2edea6 add put /:name/:tag, close #332 2014-04-18 12:18:07 +08:00
fengmk2
b59a0a99cf Release 0.4.2 2014-04-17 17:33:17 +08:00
dead_horse
c7e82809e3 Merge pull request #330 from cnpm/package-size-show
fix fav ico and show pkg size on pkg info page. fix #318
2014-04-17 17:31:06 +08:00
fengmk2
206ee505a8 sync interval config 2014-04-17 17:28:27 +08:00
fengmk2
d39838f930 fix fav ico and show pkg size on pkg info page. fix #318 2014-04-17 14:59:38 +08:00
dead_horse
512e21aaf5 Merge pull request #329 from cnpm/fix-stack-over
sync work sync one done must wait for a defer.setImmediate. fix #328
2014-04-17 14:12:22 +08:00
fengmk2
f7904ee699 sync work sync one done must wait for a defer.setImmediate. fix #328 2014-04-17 09:11:46 +08:00
dead_horse
7007be6ef4 bump dep versions 2014-04-16 15:17:14 +08:00
dead_horse
70aa3299e5 Merge pull request #326 from cnpm/noattachment
if download tarball 404, throw err better than ignore it. fixed #325
2014-04-16 12:00:29 +08:00
fengmk2
a04f5d68eb if download tarball 404, throw err better than ignore it. fixed #325 2014-04-16 11:44:28 +08:00
fengmk2
7cfe3b58ce refator sync 2014-04-16 11:19:32 +08:00
fengmk2
afdf0bc653 Merge pull request #323 from cnpm/hot-fix-status
hotfix, close #319
2014-04-16 09:39:15 +08:00
fengmk2
42055ac91e Merge pull request #324 from cnpm/hotfix-dist
hotfix, close #321
2014-04-16 09:31:48 +08:00
dead_horse
b93e0dab41 hotfix, close #321 2014-04-16 09:28:22 +08:00
dead_horse
01f2187830 hotfix, close #319 2014-04-16 09:23:59 +08:00
fengmk2
c2f49fcdd9 support custom web home page 2014-04-14 15:14:28 +08:00
dead_horse
3925ef6044 Merge pull request #316 from cnpm/first-full-sync
npm get short only can read from cnpm now
2014-04-14 14:04:59 +08:00
fengmk2
85494d2ba0 npm get short only can read from cnpm now 2014-04-14 13:57:04 +08:00
dead_horse
660ca394d6 Merge pull request #315 from cnpm/local-bindding
Local bindding and redis fix
2014-04-14 10:47:39 +08:00
fengmk2
ad7eeac00c if using reverted proxy like nginx, only binding on local host 2014-04-14 10:43:49 +08:00
fengmk2
f63b72891b fix redis detect logic 2014-04-14 10:27:46 +08:00
fengmk2
28d31b093d Release 0.4.1 2014-04-10 14:46:01 +08:00
fengmk2
466e14e92a Merge pull request #313 from cnpm/hotfix-sync-status
fix sync status code error
2014-04-10 07:46:57 +08:00
dead_horse
3646c2ce50 fix sync status code error 2014-04-10 02:39:02 +08:00
fengmk2
fa44097d8f Release 0.4.0 2014-04-09 21:05:37 +08:00
dead_horse
0c56294c47 Merge pull request #312 from cnpm/local-mysql-dev-env
Local mysql dev env
2014-04-09 20:53:12 +08:00
fengmk2
41552fd27c fix test cases to run on local machine 2014-04-09 19:37:37 +08:00
fengmk2
cb4d4f51dd add contribute guidelines 2014-04-09 09:58:23 +08:00
fengmk2
1b266d527c use local mysql for dev env. fix #308 2014-04-09 09:31:34 +08:00
fengmk2
9d660be259 Merge pull request #306 from cnpm/use-copy-to
use copy to
2014-04-01 17:37:36 +08:00
dead_horse
b041cc7361 use copy to 2014-04-01 16:19:49 +08:00
dead_horse
e420f6985e Merge pull request #305 from cnpm/fix-test
fix test case
2014-03-31 17:51:15 +08:00
fengmk2
60dc5cf5fa fix test case 2014-03-31 17:47:10 +08:00
fengmk2
54916f49e5 Merge pull request #303 from cnpm/compress-conditional
use koa-compress and koa-conditional-get
2014-03-31 14:52:39 +08:00
dead_horse
a3cafaa297 use koa-compress and koa-conditional-get 2014-03-31 14:21:13 +08:00
fengmk2
aa13a100d4 Merge pull request #302 from cnpm/typeerror-hotfix
maintainers is string, fix #301
2014-03-30 09:01:23 +08:00
fengmk2
3b29310826 maintainers is string, fix #301 2014-03-30 09:01:03 +08:00
fengmk2
6d76a590e5 Release 0.3.13 2014-03-27 16:28:01 +08:00
fengmk2
3a98b63736 Merge pull request #299 from cnpm/fix-user-update
fix npm adduser update 409 bug
2014-03-27 16:24:34 +08:00
fengmk2
f5a2090fda Merge pull request #298 from cnpm/fix-ml-cov
fix multiline coverage
2014-03-27 16:18:23 +08:00
fengmk2
989a69143f fix npm adduser update 409 bug 2014-03-27 16:18:01 +08:00
dead_horse
77e6db4f0e fix multiline coverage 2014-03-27 16:03:00 +08:00
fengmk2
a3f7affe7b Release 0.3.13 2014-03-27 15:47:19 +08:00
dead_horse
2cd0453c6b Merge pull request #297 from cnpm/pkg-engine
show package engines. fixed #280
2014-03-27 15:23:32 +08:00
fengmk2
84bc126d57 show package engines. fixed #280 2014-03-27 15:13:18 +08:00
dead_horse
760421661c Merge pull request #296 from cnpm/sync-with-local
dont sync local package field. fix #295
2014-03-27 15:04:25 +08:00
fengmk2
425e430a3a dont sync local package field. fix #295 2014-03-27 14:55:52 +08:00
fengmk2
ad3602bf15 Release 0.3.12 2014-03-26 15:28:05 +08:00
fengmk2
2c5852b344 Merge pull request #294 from cnpm/hotfix-sync
fix result.successes not exist error
2014-03-26 15:22:59 +08:00
dead_horse
115a349fc5 fix result.successes not exist error 2014-03-26 15:21:37 +08:00
fengmk2
3ceea3ccf4 Merge pull request #293 from cnpm/search-list
fix search list
2014-03-24 14:20:51 +08:00
dead_horse
ab6a9b2bea fix search list 2014-03-24 14:17:22 +08:00
fengmk2
9cc84abbb5 Merge pull request #292 from cnpm/issue291-all
only return package name in /-/all and /-/all/since, fixed #291
2014-03-24 11:27:31 +08:00
dead_horse
c1c07cf2e4 add simple request for listall 2014-03-24 11:11:54 +08:00
dead_horse
ce5c97956b only return package name in /-/all and /-/all/since, fixed #291 2014-03-24 10:31:28 +08:00
dead_horse
7cb7a517da refine docs foloder 2014-03-23 02:28:03 +08:00
dead_horse
c7df6f0f1a fix typo 2014-03-23 02:26:38 +08:00
fengmk2
ee59176b67 Merge pull request #290 from cnpm/modified-as-etag
use module gmt_modified as etag. fix #288
2014-03-22 02:09:42 +08:00
fengmk2
f160d741cb use module gmt_modified as etag. fix #288 2014-03-22 01:55:13 +08:00
dead_horse
94974f81ae fix typo, remove unused config in package.json 2014-03-21 17:26:54 +08:00
fengmk2
41c0fea3dd Merge pull request #289 from cnpm/readme
Readme
2014-03-21 16:58:27 +08:00
dead_horse
dfae519e96 fix typo 2014-03-21 16:56:15 +08:00
dead_horse
4d7d4f06b1 web page only list cnpm registry related info 2014-03-21 16:25:47 +08:00
dead_horse
5a890ff27e update readme 2014-03-21 16:15:38 +08:00
fengmk2
ac124d0e2d Merge pull request #286 from cnpm/qnfs-generator
use generator in qnfs
2014-03-20 21:17:03 +08:00
dead_horse
7e267156eb use generator in qnfs 2014-03-20 20:35:58 +08:00
fengmk2
258c34fea8 Release 0.3.11 2014-03-20 10:41:37 +08:00
fengmk2
cd9d403237 Merge pull request #284 from cnpm/issue283-maintainer
use common.isMaintainer, fixed #283
2014-03-20 10:35:55 +08:00
dead_horse
6d43612044 use common.isMaintainer, fixed #283 2014-03-20 09:49:47 +08:00
fengmk2
2e99a779c7 Merge pull request #282 from cnpm/update-thunkify
update thunkify-wrap, breaking change in thunkify-wrap
2014-03-18 10:49:39 +08:00
dead_horse
43259ed126 update dependencies 2014-03-18 10:44:21 +08:00
fengmk2
51e32e3ece Merge pull request #281 from cnpm/issue279-co-mocha
use co-mocha for test, fixed #279
2014-03-18 09:35:39 +08:00
dead_horse
37d9d62d3f use co-mocha for test, fixed #279 2014-03-17 22:27:43 +08:00
dead_horse
2e0a1ca3e8 update thunkify-wrap, breaking change in thunkify-wrap 2014-03-17 17:36:37 +08:00
fengmk2
5abaee4f2a Merge pull request #278 from cnpm/use-multiline
Use multiline
2014-03-17 14:06:59 +08:00
dead_horse
5e2ca1d5e8 refactor SQLs by using multiline 2014-03-17 13:27:09 +08:00
dead_horse
c0a6bb4869 fix typo 2014-03-16 22:28:56 +08:00
dead_horse
2aba0836cf use multiline to refactor sqls 2014-03-16 22:20:11 +08:00
fengmk2
e1636e2c7d ignore contributors 2014-03-16 16:43:58 +08:00
fengmk2
15245dc68b Release 0.3.10 2014-03-16 16:33:02 +08:00
dead_horse
8dedabaa2d Merge pull request #277 from cnpm/only-authsession-send-cookie
Only /_session request send the authSession. fixed #223
2014-03-16 16:24:35 +08:00
fengmk2
f4b82bbf0b Only /_session request send the authSession. fixed #223
* no auth requests url dont send the authSession
* baseauth dont create session cookie too
2014-03-16 16:14:12 +08:00
dead_horse
78d81c02cd Merge pull request #276 from cnpm/sync-npm-user
Sync npm user
2014-03-16 15:28:31 +08:00
fengmk2
1c743f3759 sync npm user info when maintainers and contributors not exists. fixed #82 2014-03-16 14:58:44 +08:00
fengmk2
041f80ab14 save npm user to mysql 2014-03-16 12:44:38 +08:00
fengmk2
e5646c6553 password salt always be randoms 2014-03-16 11:51:06 +08:00
fengmk2
6134983bcc Merge pull request #275 from cnpm/issue274-sync-by-install
remove session access in /name and /name/version, fixed #274
2014-03-16 11:36:27 +08:00
dead_horse
7dab569d43 remove session access in /name and /name/version, fixed #274 2014-03-15 23:20:34 +08:00
fengmk2
5aa84b03c6 Merge pull request #270 from cnpm/update-session
Update session
2014-03-15 22:59:23 +08:00
dead_horse
77fc133ecb fix update maintainer session error 2014-03-15 22:56:03 +08:00
dead_horse
f98416e217 update koa-middlewares 2014-03-15 22:52:50 +08:00
dead_horse
5f49b97aa4 fix test, fix sync_by_install 2014-03-15 22:52:50 +08:00
dead_horse
8bdfd593c9 use defer session 2014-03-15 22:52:48 +08:00
dead_horse
c214da39dc Merge pull request #272 from cnpm/author-add
Support npm owner|author add [name] [pkg]. fixed #271
2014-03-15 22:41:34 +08:00
fengmk2
f2be453ef6 Support npm owner|author add [name] [pkg]. fixed #271
* npm 1.4.4+ adduser new flow support
* npm author add, rm, ls support
2014-03-15 17:15:42 +08:00
fengmk2
e2b7003749 Release 0.3.9 2014-03-14 15:05:45 +08:00
fengmk2
944ac6a712 custom user-agent 2014-03-14 15:04:29 +08:00
dead_horse
c502c00063 Merge pull request #269 from cnpm/co-urllib
use co-urllib instead of thunkify urllib; fix mock http.request test cases
2014-03-14 14:42:12 +08:00
fengmk2
d536ad954b use co-urllib instead of thunkify urllib; fix mock http.request test cases 2014-03-14 14:33:20 +08:00
fengmk2
100706f311 request limit custom message 2014-03-13 10:47:06 +08:00
fengmk2
8d8fd31a0e Merge pull request #268 from cnpm/issue267-limit
add koa-limit, fixed #267
2014-03-13 09:55:30 +08:00
dead_horse
7aa2ea3086 add config.redis check 2014-03-13 09:31:30 +08:00
dead_horse
5bf40164a1 add koa-limit, fixed #267 2014-03-13 01:34:36 +08:00
fengmk2
e497fb354d Release 0.3.8 2014-03-11 15:13:11 +08:00
fengmk2
e647d4a1fe Merge pull request #265 from cnpm/update-middlewares
update middlewares, fixed 264
2014-03-11 15:08:33 +08:00
dead_horse
33465ecf86 update middlewares, fixed 264 2014-03-11 15:06:56 +08:00
fengmk2
3eb3eb6be9 Release 0.3.7 2014-03-11 12:02:39 +08:00
fengmk2
6ab1556ec0 show worker die date time 2014-03-10 13:50:38 +08:00
fengmk2
9fd21a5c25 Merge pull request #261 from cnpm/update-koa
update to koa@0.5.1
2014-03-08 10:35:20 +08:00
dead_horse
53b8398e93 update to koa@0.5.1 2014-03-08 10:30:27 +08:00
fengmk2
c43b117e67 hotfix for star user 2014-03-08 00:23:11 +08:00
fengmk2
7db269e5f4 Merge pull request #253 from cnpm/issue237-sync-worker
Issue237 sync worker
2014-03-07 23:48:09 +08:00
dead_horse
92ed8984d3 fix yield gather, sync missing deps even no missing versions 2014-03-07 23:45:13 +08:00
dead_horse
71d351da29 fix return versions 2014-03-07 22:36:13 +08:00
dead_horse
c8e1d8cc4a fix makefile, remove eventproxy 2014-03-07 22:36:13 +08:00
dead_horse
fd79879d0c refactor sync_module_worker 2014-03-07 22:36:13 +08:00
dead_horse
354b82f6ea refactor sync_module_worker 2014-03-07 22:36:13 +08:00
dead_horse
de0487cccc add make test-dev, fixed #259 2014-03-07 22:35:15 +08:00
dead_horse
081ccd66df change npm.js to generator 2014-03-07 22:35:15 +08:00
dead_horse
cebf501182 update urllib, proxy/npm.js use generator
but SyncModuleWorker still use callback type npm.js,
so just use co wrap to thunk.
after refactor SyncModuleWorker, remove this co wrap.
2014-03-07 22:35:14 +08:00
dead_horse
8fe593a182 sync_all and sync_exist to generator 2014-03-07 22:34:43 +08:00
dead_horse
8770458a46 change function to generator 2014-03-07 22:34:43 +08:00
dead_horse
559dc03cd4 need node >= v0.11.9 2014-03-06 16:16:36 +08:00
fengmk2
70d12f235a Release 0.3.6 2014-03-06 15:05:07 +08:00
fengmk2
aa7077e4b0 install missing package should sync it from source npm. fixed #252 2014-03-06 15:04:07 +08:00
dead_horse
f5c19668f5 Merge pull request #254 from cnpm/jshint
Add jshint check: $ make jshint
2014-03-06 14:09:07 +08:00
fengmk2
2b2a1904c6 npm publish dont contains .jshint* 2014-03-06 14:02:33 +08:00
fengmk2
a234c7e289 npm test run jshint 2014-03-06 14:00:43 +08:00
fengmk2
63562c30ec Add jshint check: $ make jshint
* .jshint make esnext true
* .jshintignore ignore no need check dirs
2014-03-06 13:55:13 +08:00
dead_horse
6968331b38 Merge pull request #251 from cnpm/yield-next
use `yield* next` instead of `yield next`
2014-03-05 16:55:32 +08:00
fengmk2
fc1a17e4d1 use yield* next instead of yield next
* According to koajs/compose#2, show performances improve a lot.
2014-03-05 16:49:58 +08:00
fengmk2
3872b7dfdf replace dist.u.qiniudn.com with cnpmjs.org/dist 2014-03-05 13:20:45 +08:00
fengmk2
e340ce081a Release 0.3.5 2014-03-05 13:18:39 +08:00
dead_horse
170a689168 Merge pull request #250 from cnpm/dist
redirect /dist/xxx.tgz => http://dist.u.qiniudn.com/xxx.tgz fixed #249
2014-03-05 11:08:05 +08:00
fengmk2
7794d1099d redirect /dist/xxx.tgz => http://dist.u.qiniudn.com/xxx.tgz fixed #249 2014-03-05 11:00:41 +08:00
dead_horse
bd8686c448 Merge pull request #247 from cnpm/redirect
redirect /name to /package/name when /name is 404. fixed #245
2014-03-04 23:54:48 +08:00
fengmk2
d8a76d03e0 redirect /name to /package/name when /name is 404. fixed #245 2014-03-04 23:22:32 +08:00
dead_horse
4460810799 Merge pull request #244 from cnpm/missing-propertys
Add missing properies and sync missing star users. fixed #235
2014-03-04 21:35:57 +08:00
fengmk2
4124525eb8 Add missing properies and sync missing star users. fixed #235
* add readmeFilename and missing created, modified time.
* Sync star users on pkg sync flow
* Show star users on registry.show
* Show star users on web package show
* Need to create new table module_star
2014-03-04 21:20:43 +08:00
fengmk2
ff50946cd3 Release 0.3.4 2014-03-04 16:32:25 +08:00
fengmk2
3637eeefdd add cov 2014-03-04 16:31:22 +08:00
dead_horse
cecb41e6ea Merge pull request #243 from cnpm/coverage
use istanbul run test coverage
2014-03-03 21:52:52 +08:00
fengmk2
72b3240ef5 use istanbul run test coverage 2014-03-03 21:02:11 +08:00
dead_horse
869d5681ce Merge pull request #242 from cnpm/gzip
gzip support. fix #241
2014-03-03 16:45:37 +08:00
fengmk2
ff1bfd5acc gzip support. fix #241 2014-03-03 16:44:29 +08:00
dead_horse
7e3129c8b8 Merge pull request #240 from stanzheng/patch-1
readme spelling patch
2014-03-03 00:42:27 +08:00
Stanley Zheng
0618c732d7 thanks dead_horse 2014-03-02 11:39:30 -05:00
Stanley Zheng
9b34ab408c readme spelling patch 2014-03-02 11:10:40 -05:00
fengmk2
d990d3aa91 Merge pull request #234 from cnpm/issue233-remove-readme
Issue233 remove readme
2014-02-28 23:25:08 +08:00
dead_horse
03ef728049 default readme to null, fixed #233 2014-02-28 22:37:45 +08:00
dead_horse
f00ed85d41 remove readme in versions 2014-02-28 22:32:44 +08:00
fengmk2
685af2a367 Release 0.3.3 2014-02-28 17:41:09 +08:00
dead_horse
818f216fb4 Merge pull request #232 from cnpm/host-hotfix
get request host from request.headers
2014-02-28 16:36:44 +08:00
fengmk2
1b47495565 get request host from request.headers 2014-02-28 16:35:51 +08:00
dead_horse
0ab314f27c Merge pull request #231 from cnpm/bug-fix
fix deps display bug#230 and nsf.url TypeError#229
2014-02-28 15:40:04 +08:00
fengmk2
95076c8787 fix deps display bug#230 and nsf.url TypeError#229 2014-02-28 15:35:37 +08:00
fengmk2
4e6eb0a9cc Release 0.3.2 2014-02-28 14:46:17 +08:00
fengmk2
29f17dd5d1 Merge pull request #228 from cnpm/update-sess
update koa-sess and koa-redis
2014-02-28 14:42:03 +08:00
dead_horse
3a48637ef1 update koa-sess and koa-redis 2014-02-28 14:40:47 +08:00
fengmk2
e1029b005f Merge pull request #227 from cnpm/fix-nfs
Fix nfs
2014-02-28 12:20:43 +08:00
dead_horse
66771dfc3b fix sync all test 2014-02-28 12:15:30 +08:00
dead_horse
66f05a2f07 remove nfs.downloadStream first, fix tmppath error 2014-02-28 12:04:28 +08:00
fengmk2
6660cabbb6 Merge pull request #226 from cnpm/giturl-bug-fix
fix fengmk2/giturl#1 bug
2014-02-27 20:53:33 +08:00
fengmk2
fbe1971957 fix fengmk2/giturl#1 bug 2014-02-27 20:52:19 +08:00
fengmk2
d75c3877bb Release 0.3.1 2014-02-27 15:06:17 +08:00
dead_horse
564ec488ea Merge pull request #225 from cnpm/etag
add etag fixed #224
2014-02-27 15:00:26 +08:00
fengmk2
1559c16c3d add etag fixed #224 2014-02-27 14:58:08 +08:00
fengmk2
2345536cbd travis ci install on source npm 2014-02-27 10:16:03 +08:00
fengmk2
89808e398b Release 0.3.0 2014-02-27 10:13:05 +08:00
fengmk2
5ddf238c08 fix typo and dont sync not exists packages 2014-02-27 10:12:53 +08:00
dead_horse
1ae193e306 Merge pull request #221 from cnpm/koa-merge
Use koa instead of connect
2014-02-27 09:57:54 +08:00
dead_horse
9d511c326c use koa-middlewares
contain some frequently-used middlewares in this module
only need to maintain this module
2014-02-27 09:51:44 +08:00
fengmk2
78d7a77b0d fix signed cookie not work on npm@1.3.25; node --harmony-generators 2014-02-27 09:51:44 +08:00
fengmk2
0f494822bc fix opensearch test case 2014-02-27 09:51:44 +08:00
fengmk2
211df84514 update koa bodyparser 2014-02-27 09:51:44 +08:00
fengmk2
fee243726e logger.error(err) should send err stack email notice 2014-02-27 09:51:44 +08:00
fengmk2
52e7e6d069 json body parse limit and bug fix.
* override json limit to default 10mb. fixed #209
 * fix #210 addPackageAndDist package version detect bug
2014-02-27 09:51:42 +08:00
dead_horse
551ae832e3 fix sync 404 reason not clear 2014-02-27 09:51:07 +08:00
dead_horse
9af99f4af2 all controllers to koa 2014-02-27 09:51:07 +08:00
dead_horse
3e8ecda9e4 controller/web/user.js to koa 2014-02-27 09:51:07 +08:00
dead_horse
fb744176f8 change web connect to koa 2014-02-27 09:51:06 +08:00
dead_horse
5e1ab4356d use outputError 2014-02-27 09:51:06 +08:00
dead_horse
5fb9a007f9 use yield exports.addPackageAndDist.call(this, next); 2014-02-27 09:51:06 +08:00
dead_horse
780a5aa158 add end() when ws write end 2014-02-27 09:51:06 +08:00
dead_horse
ab2ff4ed9e fix yield coWrite 2014-02-27 09:51:06 +08:00
dead_horse
2dad7553e6 fix all the test of registry module.test.js 2014-02-27 09:51:05 +08:00
dead_horse
acfa2e418b convert registry/module.js to koa type 2014-02-27 09:51:03 +08:00
fengmk2
74101fda7a fix auth middleware 2014-02-27 09:48:21 +08:00
fengmk2
2ec1eec91c finish registry user controller koa and update mm to support thunkify. fixed #196 2014-02-27 09:48:20 +08:00
fengmk2
b3e966184a change controllers/user.js to koa 2014-02-27 09:48:20 +08:00
dead_horse
b09960858c thunkify all proxy 2014-02-27 09:48:20 +08:00
dead_horse
84634af0c2 convert all middlewares to koa type 2014-02-27 09:48:20 +08:00
dead_horse
d60d7eaf2e change regsitry sync to koa 2014-02-27 09:48:18 +08:00
dead_horse
6d6a994997 addd koa-jsonp, koa-bodyparser, fix / controller 2014-02-27 09:46:42 +08:00
fengmk2
ab564c3b32 first koa run registry home page / 2014-02-27 09:46:39 +08:00
fengmk2
55b836388d Merge pull request #212 from cnpm/fix-sync-404
return friendly 404 reason
2014-02-26 08:56:32 +08:00
dead_horse
3f45384b74 return friendly 404 reason 2014-02-26 00:21:48 +08:00
dead_horse
6fe2997fb5 Merge pull request #211 from cnpm/bug-fix
Bug fix
2014-02-25 22:29:26 +08:00
fengmk2
cdd857ca2d override json limit to default 10mb. fixed #209 2014-02-25 20:57:54 +08:00
fengmk2
c9e513350a fix #210 addPackageAndDist package version detect bug 2014-02-25 20:56:53 +08:00
fengmk2
7b2cbd6d1d Release 0.2.27 2014-02-19 18:10:50 +08:00
fengmk2
90959ba34f Merge pull request #193 from cnpm/issue189-search-api
support json result in search, fixed #189
2014-02-19 17:42:14 +08:00
dead_horse
0f6b6a2f2b support json result in search, fixed #189 2014-02-19 17:40:44 +08:00
fengmk2
666d98d86e Release 0.2.26 2014-02-19 16:28:57 +08:00
dead_horse
e244efb153 Merge pull request #192 from cnpm/publish-add-deps
npm publish also need to add deps
2014-02-19 15:29:04 +08:00
fengmk2
67d824e5dc npm publish also need to add deps 2014-02-19 15:23:02 +08:00
fengmk2
daf29f760d Release 0.2.25 2014-02-19 14:23:11 +08:00
dead_horse
e5939d170f Merge pull request #191 from cnpm/module_deps
Dependents support. fixed #190
2014-02-19 14:15:01 +08:00
fengmk2
3526c9eff7 max handle number of package.json dependencies property 2014-02-19 14:07:06 +08:00
fengmk2
973889c73a Dependents support. fixed #190 2014-02-19 13:29:33 +08:00
fengmk2
70cd5f5cb6 Release 0.2.24 2014-02-13 18:43:13 +08:00
fengmk2
ba6139f265 Merge pull request #187 from cnpm/issue186-remvoe
refactor remove module, fixed #186
2014-02-13 18:41:49 +08:00
dead_horse
645e4913ba fix if delete all the versions 2014-02-13 17:34:56 +08:00
dead_horse
46eba8d03a refactor remove module, fixed #186
1. list all the modules, find which versions to be removed and which to
be remained.
2. remove all the modules need to be removed
3. list all the tags, find which tags need to be removed and remove
them.
4. if the latest tag removed, need to generate a new latest tag.
2014-02-13 14:51:29 +08:00
fengmk2
9d2a649aac Release 0.2.23 2014-01-26 16:58:30 +08:00
dead_horse
bf53537f00 Merge pull request #184 from cnpm/admin-role-fix
system admin can add, publish, remove the packages. fixed #176
2014-01-26 00:52:15 -08:00
fengmk2
1047e18732 system admin can add, publish, remove the packages. fixed #176 2014-01-26 16:39:01 +08:00
fengmk2
2981a17b10 Release 0.2.22 2014-01-26 16:22:30 +08:00
dead_horse
041416abf1 Merge pull request #183 from cnpm/keyword-search
add keyword and search support keyword. #181
2014-01-26 00:19:06 -08:00
fengmk2
c117b67f3b add keyword and search support keyword. #181 2014-01-26 15:25:05 +08:00
fengmk2
bc2ac45588 Release 0.2.21 2014-01-24 16:37:40 +08:00
fengmk2
95eba3fcfc refactor code styles on package.html 2014-01-24 16:31:40 +08:00
fengmk2
e98b409f61 Merge pull request #180 from 4simple/master
nav-tabs support in package page
2014-01-24 00:19:04 -08:00
dead_horse
fedec47349 Merge pull request #179 from cnpm/show-server-error
Show registry server error response. fixed #178
2014-01-24 00:11:36 -08:00
4simple
b63a565a37 nav-tabs e.preventDefault 2014-01-24 15:58:49 +08:00
fengmk2
67eb7fa380 Show registry server error response. fixed #178 2014-01-24 15:22:07 +08:00
4simple
a117886b71 nav-tabs for package.html 2014-01-24 00:22:24 +08:00
fengmk2
152f6800da Release 0.2.20 2014-01-23 14:31:34 +08:00
dead_horse
2f7e52f86f hotfix check missing readme error 2014-01-23 14:29:20 +08:00
dead_horse
bb4b51c54f hotfix sync missing dependencies and readmes 2014-01-23 14:26:30 +08:00
fengmk2
4c2303e4fa Merge pull request #175 from cnpm/issue174-readme
Issue174 readme
2014-01-22 22:17:00 -08:00
dead_horse
b70201db79 fix sync readme error, fixed #174 2014-01-23 14:16:26 +08:00
dead_horse
dd095147b0 add updateReadme in module 2014-01-23 14:07:39 +08:00
fengmk2
08c4445e78 Release 0.2.19 2014-01-22 16:45:28 +08:00
dead_horse
b892ad8185 Merge pull request #173 from cnpm/no-auth-on-install
npm install no need to check authorization header. fixed #171
2014-01-22 00:32:21 -08:00
fengmk2
a8ee1e43bf npm install no need to check authorization header. fixed #171 2014-01-22 16:31:17 +08:00
fengmk2
dd84674b98 Release 0.2.18 2014-01-20 21:43:07 +08:00
dead_horse
ccec2cb45b Merge pull request #170 from cnpm/show-git-web-url
Support gitlab git url to display and click. fixed #160
2014-01-20 05:34:09 -08:00
fengmk2
f3a4500191 Support gitlab git url to display and click. fixed #160 2014-01-20 21:15:48 +08:00
fengmk2
161a860f74 fix redis crash 2014-01-19 09:55:15 +08:00
fengmk2
f58e8a24b0 Release 0.2.17 2014-01-17 11:42:42 +08:00
fengmk2
9bc6a6dd74 custom logo url 2014-01-17 11:42:14 +08:00
fengmk2
c50364807d Merge pull request #167 from cnpm/hotfix-layout
hotfix layout bug
2014-01-16 19:05:44 -08:00
dead_horse
b3e3ecfc3a hotfix layout bug 2014-01-17 11:03:18 +08:00
fengmk2
637b5fe785 Release 0.2.16 2014-01-16 18:18:37 +08:00
dead_horse
6eed7b9002 Merge pull request #166 from cnpm/update-publish-time
fix publish-time bug
2014-01-16 02:10:11 -08:00
fengmk2
899becb448 fix publish-time bug 2014-01-16 18:09:03 +08:00
fengmk2
7e982ca877 Release 0.2.15 2014-01-16 17:12:54 +08:00
fengmk2
26156253c7 add publish_time to debug 2014-01-16 17:12:32 +08:00
fengmk2
9445d46bbf Release 0.2.14 2014-01-16 16:05:52 +08:00
fengmk2
55bfcc853e Merge pull request #164 from cnpm/issue163-time-error
update publish_time, fixed #163
2014-01-15 23:58:42 -08:00
fengmk2
4eeb1b25de Merge pull request #165 from cnpm/add-autod
add make autod
2014-01-15 23:58:02 -08:00
dead_horse
42959196c6 add make autod 2014-01-16 15:17:37 +08:00
dead_horse
d264b47b09 update publish_time, fixed #163 2014-01-16 14:58:55 +08:00
fengmk2
4e412d1808 Release 0.2.13 2014-01-15 18:48:37 +08:00
dead_horse
73527da1f1 Merge pull request #162 from cnpm/fix-custom-footer
markdown tmpl not support footer, need to wrap on app start
2014-01-15 02:47:26 -08:00
fengmk2
7986f198af markdown tmpl not support footer, need to wrap on app start 2014-01-15 18:27:07 +08:00
fengmk2
adb1411313 Release 0.2.12 2014-01-15 18:05:36 +08:00
dead_horse
2f24e9828e Merge pull request #161 from cnpm/more-custom
add footer and npm client name customable
2014-01-15 01:52:21 -08:00
fengmk2
d7e5921e24 add footer and npm client name customable 2014-01-15 17:44:41 +08:00
fengmk2
e3e6a1aaa5 Release 0.2.11 2014-01-15 14:52:28 +08:00
dead_horse
0d1969fadd Merge pull request #158 from cnpm/packagePageContributorSearch
package page contributor link to search, default is true
2014-01-14 22:50:28 -08:00
fengmk2
028e599d51 package page contributor link to search, default is true 2014-01-15 14:46:06 +08:00
fengmk2
fa0dd5c23f Release 0.2.10 2014-01-14 20:57:14 +08:00
dead_horse
7be5df8e70 Merge pull request #156 from cnpm/fix-Disposition
fix #155 Content-Disposition wrong.
2014-01-14 04:29:34 -08:00
fengmk2
adddf0e4c5 fix #155 Content-Disposition wrong. 2014-01-14 20:24:22 +08:00
fengmk2
28cc13d583 Release 0.2.9 2014-01-14 15:41:55 +08:00
dead_horse
c85b27b9b2 Merge pull request #154 from cnpm/couch-search-api
support couch db search api. fixed #153
2014-01-13 23:22:22 -08:00
fengmk2
46795adf54 support startkey=c and startkey="c" 2014-01-14 15:17:04 +08:00
fengmk2
c8ab1735a5 support couch db search api. fixed #153 2014-01-14 15:14:30 +08:00
dead_horse
4c759b40c8 Merge pull request #152 from cnpm/sync-by-query-name
support sync by query.name
2014-01-13 22:20:05 -08:00
fengmk2
1bab099f38 fix fork me image link 2014-01-14 13:55:47 +08:00
fengmk2
f95f814a8c support sync by query.name 2014-01-14 13:54:48 +08:00
fengmk2
ffcb0d669a Release 0.2.8 2014-01-14 10:14:56 +08:00
dead_horse
81ca81d578 Merge pull request #150 from cnpm/download-link
add download link for package page
2014-01-13 18:11:22 -08:00
fengmk2
803f6d42f8 dont show err stack on test env 2014-01-14 09:59:39 +08:00
fengmk2
5366f16bcb add download link for package page 2014-01-14 09:49:15 +08:00
fengmk2
ab18e49131 Release 0.2.7 2014-01-13 21:53:55 +08:00
fengmk2
3c874244a7 Merge pull request #149 from cnpm/issue148-shasum
add shasum when nfs.upload and hfs.uploadBuffer, fixed #148
2014-01-13 05:52:18 -08:00
dead_horse
d463b09c81 add shasum when nfs.upload and hfs.uploadBuffer, fixed #148 2014-01-13 21:50:22 +08:00
fengmk2
40301f260b Release 0.2.6 2014-01-13 17:15:32 +08:00
fengmk2
72dbde1906 Merge pull request #147 from cnpm/issue146-session-config
support custom session store
2014-01-13 01:13:56 -08:00
dead_horse
d68157faa8 support custom session store, fixed #146 2014-01-13 17:09:04 +08:00
fengmk2
c88991c028 Release 0.2.5 2014-01-13 12:00:47 +08:00
dead_horse
262abe8520 Merge pull request #144 from cnpm/download-stream
nfs download to a writeable stream.
2014-01-12 18:50:50 -08:00
fengmk2
9dd942df1a add download timeout and unit test 2014-01-13 10:44:55 +08:00
fengmk2
9ec552e08d use downloadStream() first 2014-01-13 09:48:02 +08:00
fengmk2
2420164a9d nfs download to a writeable stream.
nfs.download(key, writeStream, cb), should not create a tmp file,
and also add content-length, content-type headers.
2014-01-11 00:14:17 +08:00
fengmk2
822f2f6a4e Release 0.2.4 2014-01-10 15:59:56 +08:00
fengmk2
d423a987ae Merge pull request #143 from cnpm/issue142-module
set main script to  index.js, fixed #142
2014-01-09 23:59:07 -08:00
dead_horse
70cefd817e set main script to index.js, fixed #142 2014-01-10 15:52:32 +08:00
fengmk2
2606af24a1 Release 0.2.3 2014-01-10 15:15:59 +08:00
dead_horse
5b781cbda1 Merge pull request #140 from cnpm/package-display
dont show sync button on private package
2014-01-09 23:05:13 -08:00
fengmk2
1519b52a74 dont show sync button on private package 2014-01-10 14:38:23 +08:00
dead_horse
4b73f6e0a7 Merge pull request #139 from cnpm/sync-as-publish-with-no-deps
Sync package as publish with no deps. fixed #138
2014-01-09 21:09:33 -08:00
fengmk2
88bcb14a4f Sync package as publish with no deps. fixed #138
Let user sync their private packages from private registry to other.
Just use `$ cnpm sync --publish --no-deps private_pkg1 private_pkg2`.
2014-01-10 12:46:38 +08:00
fengmk2
5bd2cf5d50 Release 0.2.2 2014-01-10 10:58:35 +08:00
fengmk2
e8fcfc67a8 Merge pull request #137 from fengmk2/issue136-nfs-support
Issue136 nfs support
2014-01-09 18:48:53 -08:00
dead_horse
580fc8c777 keep compatibility
temp save path add random string
add download statistics
add if else after ufs.upload
2014-01-10 10:39:47 +08:00
dead_horse
60fdcdd0e4 qnfs upload only callback a url 2014-01-09 23:11:40 +08:00
dead_horse
09fdff49a1 compat remove package
use dist.tarball to store the key, avoid to change mysql
2014-01-09 18:01:43 +08:00
dead_horse
2f1b5034c4 set tarball url 2014-01-09 17:27:14 +08:00
dead_horse
6571ec53bb fix test 2014-01-09 16:11:31 +08:00
dead_horse
4a106f1bdf new npm publish in one request, add _publish_in_cnpm 2014-01-09 16:08:56 +08:00
dead_horse
92c20c90ac support unsure name ufs
upload return {key: 'xxx'}, then save it
2014-01-09 16:08:56 +08:00
fengmk2
31a920c5b3 contributors maybe a object 2014-01-08 11:23:54 +08:00
dead_horse
a3b51c6803 Merge pull request #135 from fengmk2/hotfix
Object #<Object> has no method 'forEach' fixed #134
2014-01-07 19:18:53 -08:00
fengmk2
2482c06e04 Object #<Object> has no method 'forEach' fixed #134 2014-01-08 11:17:51 +08:00
fengmk2
bbafa027e8 Merge pull request #133 from fengmk2/issue132-config
support custom config as a module, fixed issue #132
2014-01-07 19:10:52 -08:00
dead_horse
3ca62486d2 support custom config as a module, fixed issue #132 2014-01-07 21:27:33 +08:00
dead_horse
6371d60b70 Merge pull request #130 from fengmk2/fix-publish
Fix new npm publish flow
2013-12-27 05:59:15 -08:00
fengmk2
8014e0281f support npm new publish flow. fixed #129 2013-12-27 21:43:41 +08:00
fengmk2
253c11c855 add toString and constructor to test admin 2013-12-27 19:05:57 +08:00
fengmk2
49c5c73281 fix #119 hasOwnProperty check admin bug. 2013-12-27 19:04:02 +08:00
fengmk2
684edc1a28 Release 0.2.0 2013-12-27 18:37:01 +08:00
dead_horse
cc60007679 Merge pull request #128 from fengmk2/module-name-case-care
fix #127 execSync and execsync.
2013-12-27 02:18:43 -08:00
fengmk2
6788180ca7 remove to lower case 2013-12-27 18:07:58 +08:00
fengmk2
57c9b2544b fix #127 execSync and execsync. 2013-12-27 18:04:49 +08:00
dead_horse
c91995644c Merge pull request #126 from fengmk2/contributors
add contributors list on package page
2013-12-26 23:08:50 -08:00
fengmk2
41fa1ad524 add contributors list on package page 2013-12-27 15:00:13 +08:00
dead_horse
af14f03932 Merge pull request #125 from fengmk2/sync-typeerror
sync typeerror fix #statusCode
2013-12-24 22:42:58 -08:00
fengmk2
5a0b4fc351 mv blanket to config 2013-12-25 14:38:15 +08:00
fengmk2
f42a2bbeef sync typeerror fix #statusCode 2013-12-25 14:31:45 +08:00
fengmk2
8980e98a7d add disturl 2013-12-23 17:19:58 +08:00
fengmk2
b401ae2772 fix #122 admin security bug 2013-12-22 11:34:12 +08:00
fengmk2
e56fcdbdc4 fixed #121, let pkg 404 as success 2013-12-21 09:10:05 +08:00
fengmk2
4d7f6906dd Merge pull request #120 from fengmk2/fix-sql
fix sql insert error
2013-12-20 16:53:02 -08:00
dead_horse
f4b5977ad9 fix sql insert error 2013-12-21 01:42:33 +08:00
fengmk2
98c84e1556 fix typos 2013-12-20 17:52:01 +08:00
293 changed files with 26115 additions and 5809 deletions

2
.dockerignore Normal file
View File

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

17
.editorconfig Normal file
View File

@@ -0,0 +1,17 @@
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab

3
.eslintrc Normal file
View File

@@ -0,0 +1,3 @@
{
"extends": "eslint-config-egg"
}

12
.gitignore vendored
View File

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

View File

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

View File

@@ -1,5 +0,0 @@
language: node_js
node_js:
- '0.10'
install: npm install --registry=http://registry.cnpmjs.org
script: make test-coveralls

13
AUTHORS
View File

@@ -1,7 +1,12 @@
# Ordered by date of first contribution.
# Auto-generated by 'contributors' on Fri, 20 Dec 2013 09:33:09 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)
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)

39
CONTRIBUTING.md Normal file
View File

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

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"]

1441
History.md

File diff suppressed because it is too large Load Diff

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

107
Makefile
View File

@@ -1,30 +1,105 @@
TESTS = $(shell ls -S `find test -type f -name "*.test.js" -print`)
REPORTER = tap
TIMEOUT = 30000
REPORTER = spec
TIMEOUT = 600000
MOCHA_OPTS =
DB = sqlite
install:
@npm install --registry=http://registry.cnpmjs.org --cache=${HOME}/.npm/.cache/cnpm
jshint:
@node_modules/.bin/jshint .
test: install
@NODE_ENV=test ./node_modules/mocha/bin/mocha \
init-database:
@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;'
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 \
--reporter $(REPORTER) \
--timeout $(TIMEOUT) \
--require intelli-espower-loader \
--require should \
--require thunk-mocha \
--require ./test/init.js \
$(MOCHA_OPTS) \
$(TESTS)
test-cov:
@$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=travis-cov
test-sqlite:
@$(MAKE) test DB=sqlite
test-cov-html:
@rm -f coverage.html
@$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=html-cov > coverage.html
@ls -lh coverage.html
test-mysql: init-mysql
@$(MAKE) test DB=mysql
test-coveralls: test
@echo TRAVIS_JOB_ID $(TRAVIS_JOB_ID)
@-$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=mocha-lcov-reporter | ./node_modules/coveralls/bin/coveralls.js
test-pg: init-pg
@DB_PORT=5432 DB_USER=$(USER) $(MAKE) test DB=postgres
test-all: test test-cov
test-all: test-sqlite test-mysql
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 thunk-mocha \
--require ./test/init.js \
$(MOCHA_OPTS) \
$(TESTS)
test-cov-sqlite:
@$(MAKE) test-cov DB=sqlite
test-cov-mysql: init-mysql
@$(MAKE) test-cov DB=mysql
test-travis: init-database
@NODE_ENV=test DB=${DB} \
node \
node_modules/.bin/istanbul cover \
node_modules/.bin/_mocha \
-- -u exports \
--reporter dot \
--timeout $(TIMEOUT) \
--require intelli-espower-loader \
--require should \
--require thunk-mocha \
--require ./test/init.js \
$(MOCHA_OPTS) \
$(TESTS)
test-travis-sqlite:
@$(MAKE) test-travis DB=sqlite
test-travis-mysql: init-mysql
@$(MAKE) test-travis DB=mysql
test-travis-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 node_modules/.bin/node-dev dispatch.js
contributors:
@node_modules/.bin/contributors -f plain -o AUTHORS
autod:
@node_modules/.bin/autod -w \
--registry https://r.cnpmjs.org \
--prefix "^" \
--exclude public,view,docs,backup,coverage \
--dep mysql \
--keep should,supertest,chunkstream,mm,pedding
.PHONY: test

202
README.md
View File

@@ -1,69 +1,179 @@
cnpmjs.org
=======
[![Build Status](https://secure.travis-ci.org/fengmk2/cnpmjs.org.png)](http://travis-ci.org/fengmk2/cnpmjs.org) [![Coverage Status](https://coveralls.io/repos/fengmk2/cnpmjs.org/badge.png)](https://coveralls.io/r/fengmk2/cnpmjs.org)[![Dependency Status](https://gemnasium.com/fengmk2/cnpmjs.org.png)](https://gemnasium.com/fengmk2/cnpmjs.org)
[![npm version][npm-image]][npm-url]
[![build status][travis-image]][travis-url]
[![Test coverage][codecov-image]][codecov-url]
[![Known Vulnerabilities][snyk-image]][snyk-url]
[![npm download][download-image]][download-url]
[![NPM](https://nodei.co/npm/cnpmjs.org.png?downloads=true&stars=true)](https://nodei.co/npm/cnpmjs.org/)
[npm-image]: http://cnpmjs.org/badge/v/cnpmjs.org.svg?style=flat-square
[npm-url]: http://cnpmjs.org/package/cnpmjs.org
[travis-image]: https://img.shields.io/travis/cnpm/cnpmjs.org.svg?style=flat-square
[travis-url]: https://travis-ci.org/cnpm/cnpmjs.org
[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
![logo](https://raw.github.com/fengmk2/cnpmjs.org/master/logo.png)
![logo](https://raw.github.com/cnpm/cnpmjs.org/master/logo.png)
## What is this?
## Description
Private npm registry and web for Enterprise, base on MySQL and Simple File Store.
Private npm registry and web for Enterprise, base on [koa](http://koajs.com/),
MySQL and [Simple Store Service](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide).
@[JacksonTian](https://github.com/JacksonTian/) had a talk about [private npm](https://speakerdeck.com/jacksontian/qi-ye-ji-node-dot-jskai-fa).
Our goal is to provide a low cost maintenance, easy to use, and easy to scale solution for private npm.
![cnpm](https://docs.google.com/drawings/d/12QeQfGalqjsB77mRnf5Iq5oSXHCIUTvZTwECMonqCmw/pub?w=480&h=360)
## What can you do with `cnpmjs.org`?
## Install
* Build a private npm for your own enterprise. ([alibaba](http://www.alibaba.com/) is using `cnpmjs.org` now)
* 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
* **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).
* **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 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.
## Docs
* [How to deploy](https://github.com/cnpm/cnpmjs.org/wiki/Deploy)
* cnpm client: [cnpm](https://github.com/cnpm/cnpm), `npm install -g cnpm`
* [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) >= 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/) >= 5.6.16, include `mysqld` and `mysql cli`. I test on `mysql@5.6.16`.
* MariaDB
* PostgreSQL
### Clone code and run test
```bash
$ npm install
# clone from git
$ git clone https://github.com/cnpm/cnpmjs.org.git
# install dependencies
$ make install
# test
$ make test
# coverage
$ make test-cov
# update dependencies
$ make autod
# start server with development mode
$ make dev
```
## Usage
### Dockerized cnpmjs.org Installation Guide
```js
$ node dispatch.js
```
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.
## Authors
#### 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
$ git summary
$docker-compose up
```
project : cnpmjs.org
repo age : 2 weeks
active : 81 days
commits : 205
files : 83
authors :
120 fengmk2 58.5%
84 dead_horse 41.0%
1 Alsotang 0.5%
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
* Checkout a new branch
* Add new features or fix bugs in the new branch
* Make a pull request and we will review it ASAP
Tips: make sure your code is following the [node-style-guide](https://github.com/felixge/node-style-guide).
## 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)

6
app/model/README.md Normal file
View File

@@ -0,0 +1,6 @@
# Model
## Test
- add `TRUNCATE` script on `test/.setup.js`.
- add test case on `test/app/model/*.test.js` for the new model, please follow `test/app/model/user.test.js` example.

24
app/model/user.js Normal file
View File

@@ -0,0 +1,24 @@
'use strict';
module.exports = (app, Model) => {
const { STRING, TEXT, BIGINT, BOOLEAN } = Model.DataTypes;
class User extends Model {}
User.init({
id: { type: BIGINT.UNSIGNED, autoIncrement: true, primaryKey: true },
name: { type: STRING(100), allowNull: false },
salt: { type: STRING(100), allowNull: false },
passwordSha: { type: STRING(100), allowNull: false },
ip: { type: STRING(64), allowNull: false },
roles: { type: STRING(200), allowNull: false, defaultValue: '[]' },
rev: { type: STRING(40), allowNull: false },
email: { type: STRING(400), allowNull: false },
json: { type: TEXT, allowNull: true },
isNpmUser: { type: BOOLEAN, allowNull: false, defaultValue: false, column: 'npm_user' },
}, {
table: 'user',
});
return User;
};

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
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

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,70 +1,56 @@
/*!
* cnpmjs.org - common/logger.js
* Copyright(c) 2013
* Author: dead_horse <undefined>
*/
'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 config = require('../config');
var util = require('util');
var moment = require('moment');
var logstream = require('logfilestream');
var ms = require('ms');
const isTEST = process.env.NODE_ENV === 'test';
const categories = ['sync_info', 'sync_error'];
var isTEST = process.env.NODE_ENV === 'test';
var ONE_DAY = ms('1d');
var levels = ['info', 'warn', 'error'];
levels.forEach(function (catetory) {
var options = {
logdir: config.logdir,
duration: ONE_DAY,
nameformat: '[' + catetory + '.]YYYY-MM-DD[.log]'
};
var stream = logstream(options);
function write(msg) {
var time = moment().format('YYYY-MM-DD HH:mm:ss.SSS');
if (msg instanceof Error) {
var err = {
name: msg.name,
code: msg.code,
message: msg.message,
stack: msg.stack,
host: msg.host,
url: msg.url,
data: msg.data
};
if (err.name === 'Error' && typeof err.code === 'string') {
err.name = err.code + err.name;
}
err.name += 'Exception';
if (err.host) {
err.message += ' (' + err.host + ')';
}
msg = util.format('%s nodejs.%s: %s\nURL: %s\nData: %j\n%s\n\n',
time,
err.name,
err.stack,
err.url,
err.data,
time
);
} else {
msg = time + ' ' + util.format.apply(util, arguments) + '\n';
}
if (!isTEST) {
if (config.debug) {
var level = catetory;
console.log('[' + level + '] ' + msg);
} else {
stream.write(msg);
}
}
}
exports[catetory] = write;
const logger = module.exports = Logger({
categories: categories,
dir: config.logdir,
duration: '1d',
format: '[{category}.]YYYY-MM-DD[.log]',
stdout: config.debug && !isTEST,
errorFormater: errorFormater,
seperator: os.EOL,
});
const to = [];
for (var user in config.admins) {
to.push(config.admins[user]);
}
function errorFormater(err) {
const msg = formater.both(err);
mail.error(to, msg.json.name, msg.text);
return msg.text;
}
logger.syncInfo = function () {
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 () {
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,24 +1,31 @@
/*!
* 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 utility = require('utility');
var nodemailer = require('nodemailer');
var utility = require('utility');
var os = require('os');
var mailConfig = require('../config').mail;
var smtpConfig;
if (mailConfig.auth) {
// new style
smtpConfig = mailConfig;
} else {
smtpConfig = {
enable: mailConfig.enable,
// backward compat
host: mailConfig.host,
port: mailConfig.port,
secure: mailConfig.secure || mailConfig.ssl,
debug: mailConfig.debug,
auth: {
user: mailConfig.user,
pass: mailConfig.pass
}
};
}
var transport;
/**
* Send notice email with mail level and appname.
*
@@ -51,26 +58,23 @@ LEVELS.forEach(function (level) {
exports.send = function (to, subject, html, callback) {
callback = callback || utility.noop;
var transport = nodemailer.createTransport("SMTP", {
host: mailConfig.host,
port: mailConfig.port,
secureConnection: mailConfig.ssl,
debug: mailConfig.debug,
auth: {
user: mailConfig.user,
pass: mailConfig.pass,
}
});
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 = {
sender: mailConfig.sender,
from: mailConfig.from || mailConfig.sender,
to: to,
subject: subject,
html: html,
};
transport.sendMail(message, function (err, result) {
transport.close();
callback(err, result);
});
};

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

View File

@@ -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';
@@ -15,11 +13,5 @@
*/
var config = require('../config');
var nfs = config.nfs;
if (!nfs) {
// use qnfs by default
nfs = require('./qnfs');
}
module.exports = nfs;
module.exports = config.nfs;

View File

@@ -1,45 +0,0 @@
/*!
* cnpmjs.org - common/qnfs.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var qn = require('qn');
var config = require('../config');
var client = qn.create(config.qn);
/**
* Upload file
*
* @param {String} filepath
* @param {Object} options
* - {String} key
* - {Number} size
* @param {Function(err, result)} callback
* - {Object} result
* - {String} url
*/
exports.upload = function (filepath, options, callback) {
client.delete(options.key, function (err, data) {
client.uploadFile(filepath, {key: options.key, size: options.size}, callback);
});
};
exports.url = function (key) {
return client.resourceURL(key);
};
exports.remove = function (key, callback) {
client.delete(key, callback);
};

65
common/sequelize.js Normal file
View File

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

View File

@@ -1,41 +0,0 @@
/*!
* cnpmjs.org - common/session.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
'use strict';
/**
* Module dependencies.
*/
var connect = require('connect');
var RedisStore = require('connect-redis')(connect);
var config = require('../config');
var session;
var key = 'AuthSession';
var cookie = { path: '/', httpOnly: true, maxAge: 3600000 * 24 * 30 };
if (config.debug) {
session = connect.cookieSession({
secret: config.sessionSecret,
key: key,
cookie: cookie
});
} else {
session = connect.session({
key: key,
secret: config.sessionSecret,
store: new RedisStore(config.redis),
cookie: cookie,
});
}
module.exports = session;

61
common/urllib.js Normal file
View File

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

17
config/config.default.js Normal file
View File

@@ -0,0 +1,17 @@
'use strict';
module.exports = () => {
const { env } = process;
return {
orm: {
delegate: 'orm',
client: env.CNPMJS_DATABASE_CLIENT || 'mysql',
database: env.CNPMJS_DATABASE_NAME || 'cnpmjs_dev',
host: env.CNPMJS_DATABASE_HOST || 'localhost',
port: env.CNPMJS_DATABASE_PORT || 3306,
user: env.CNPMJS_DATABASE_USER || 'root',
password: env.CNPMJS_DATABASE_PASSWORD || '',
},
};
};

View File

@@ -1,96 +1,336 @@
/*!
* 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 mkdirp = require('mkdirp');
var os = require('os');
var utility = require('utility');
fs.existsSync = fs.existsSync || path.existsSync;
var pkg = require('../package.json');
var version = require('../package.json').version;
var root = path.dirname(__dirname);
var dataDir = path.join(process.env.HOME || root, '.cnpmjs.org');
var config = {
version: pkg.version,
version: version,
dataDir: dataDir,
/**
* Cluster mode
*/
enableCluster: false,
numCPUs: os.cpus().length,
/*
* server configure
*/
registryPort: 7001,
webPort: 7002,
enableCluster: false,
debug: true, // if debug
logdir: path.join(root, '.tmp', 'logs'),
viewCache: false,
// mysql config
mysqlServers: [
{
host: 'keydiary.mysql.rds.aliyuncs.com', // 'db4free.net'
port: 3306,
user: 'cnpmjs',
password: 'cnpmjs123'
}
],
mysqlDatabase: 'cnpmjstest',
mysqlMaxConnections: 4,
mysqlQueryTimeout: 5000,
bindingHost: '127.0.0.1', // only binding on 127.0.0.1 for local access
// default is ctx.protocol
protocol: '',
// 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',
redis: {
host: 'pub-redis-19533.us-east-1-4.3.ec2.garantiadata.com',
port: 19533,
pass: 'cnpmjs_dev'
},
uploadDir: path.join(root, 'public', 'dist'),
// qiniu cdn: http://www.qiniu.com/, it free for dev.
qn: {
accessKey: "iN7NgwM31j4-BZacMjPrOQBs34UG1maYCAQmhdCV",
secretKey: "6QTOr2Jg1gcZEWDQXKOGZh5PziC2MCV5KsntT70j",
bucket: "qtestbucket",
domain: "http://qtestbucket.qiniudn.com"
// max request json body size
jsonLimit: '10mb',
// log dir name
logdir: path.join(dataDir, 'logs'),
// update file template dir
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: {
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',
},
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
},
enableCompress: false, // enable gzip response or not
sourceNpmRegistry: 'http://registry.npmjs.org',
enablePrivate: true, // enable private mode, only admin can publish, other use just can sync package from source npm
// default system admins
admins: {
admin: 'admin@cnpmjs.org',
// name: email
fengmk2: 'fengmk2@gmail.com',
admin: 'admin@cnpmjs.org',
dead_horse: 'dead_horse@qq.com',
cnpmjstest10: 'cnpmjstest10@cnpmjs.org',
},
// 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: '',
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`
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: '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
},
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/data.sqlite
storage: path.join(dataDir, 'data.sqlite'),
logging: !!process.env.SQL_DEBUG,
},
// 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(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',
/**
* 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',
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: '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
sourceNpmRegistryIsCNpm: true,
// if install return 404, try to sync from source registry
syncByInstall: true,
backupFilePrefix: '/cnpm/backup/', // backup filepath prefix
// sync mode select
// none: do not sync any module, 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,
// try to remove all deleted versions from original registry
syncDeletedVersions: true,
// 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,
},
};
// load config/config.js, everything in config.js will cover the same key in index.js
var customConfig = path.join(root, 'config/config.js');
if (fs.existsSync(customConfig)) {
var options = require(customConfig);
for (var k in options) {
config[k] = options[k];
if (process.env.NODE_ENV === 'test') {
config.enableAbbreviatedMetadata = true;
config.customRegistryMiddlewares.push(() => {
return function* (next) {
this.set('x-custom-middleware', 'true');
yield next;
};
});
config.customWebMiddlewares.push(() => {
return function* (next) {
this.set('x-custom-web-middleware', 'true');
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);
}
}
@@ -98,3 +338,10 @@ mkdirp.sync(config.logdir);
mkdirp.sync(config.uploadDir);
module.exports = config;
config.loadConfig = function (customConfig) {
if (!customConfig) {
return;
}
copy(customConfig).override(config);
};

6
config/plugin.js Normal file
View File

@@ -0,0 +1,6 @@
'use strict';
exports.orm = {
enable: true,
package: 'egg-orm',
};

View File

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

View File

@@ -1,635 +0,0 @@
/**!
* cnpmjs.org - controllers/registry/module.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var debug = require('debug')('cnpmjs.org:controllers:registry:module');
var path = require('path');
var fs = require('fs');
var crypto = require('crypto');
var utility = require('utility');
var eventproxy = require('eventproxy');
var Bagpipe = require('bagpipe');
var urlparse = require('url').parse;
var config = require('../../config');
var Module = require('../../proxy/module');
var Total = require('../../proxy/total');
var nfs = require('../../common/nfs');
var common = require('../../lib/common');
var Log = require('../../proxy/module_log');
var DownloadTotal = require('../../proxy/download');
var SyncModuleWorker = require('../../proxy/sync_module_worker');
var logger = require('../../common/logger');
var semver = require('semver');
exports.show = function (req, res, next) {
var name = req.params.name;
var ep = eventproxy.create();
ep.fail(next);
Module.listTags(name, ep.done('tags'));
Module.listByName(name, ep.done('rows'));
ep.all('tags', 'rows', function (tags, rows) {
debug('show module, user: %s, allowSync: %s, isAdmin: %s', req.session.name, req.session.allowSync, req.session.isAdmin);
// if module not exist in this registry,
// sync the module backend and return package info from official registry
// TODO: Need to change this logic, it will cause unpublish sync again.
// TODO: Hard to detect this show is on install flow or unpublish flow
if (rows.length === 0) {
if (!req.session.allowSync) {
return next();
}
var username = (req.session && req.session.name) || 'anonymous';
SyncModuleWorker.sync(name, username, function (err, result) {
if (err) {
return next(err);
}
if (!result.ok) {
return res.json(result.statusCode, result.pkg);
}
res.json(200, result.pkg);
});
return;
}
var nextMod = null;
var latestMod = null;
var distTags = {};
for (var i = 0; i < tags.length; i++) {
var t = tags[i];
distTags[t.tag] = t.version;
}
var versions = {};
var times = {};
var attachments = {};
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
if (row.version === 'next') {
nextMod = row;
continue;
}
var pkg = row.package;
common.downloadURL(pkg, req);
versions[pkg.version] = pkg;
times[pkg.version] = row.publish_time ? new Date(row.publish_time) : row.gmt_modified;
if ((!distTags.latest && !latestMod) || distTags.latest === row.version) {
latestMod = row;
}
}
if (!latestMod) {
latestMod = nextMod || rows[0];
}
if (!nextMod) {
nextMod = latestMod;
}
var rev = '';
if (nextMod) {
rev = String(nextMod.id);
}
var info = {
_id: name,
_rev: rev,
name: name,
description: latestMod.package.description,
"dist-tags": distTags,
maintainers: latestMod.package.maintainers,
time: times,
author: latestMod.package.author,
repository: latestMod.package.repository,
versions: versions,
readme: latestMod.package.readme,
_attachments: attachments,
};
debug('show module %s: %s, latest: %s', name, rev, latestMod.version);
res.json(info);
});
};
exports.get = function (req, res, next) {
var name = req.params.name;
var tag = req.params.version;
var version = semver.valid(tag);
var ep = eventproxy.create();
ep.fail(next);
var method = version ? 'get' : 'getByTag';
var queryLabel = version ? version : tag;
Module[method](name, queryLabel, ep.done(function (mod) {
if (mod) {
common.downloadURL(mod.package, req);
return res.json(mod.package);
}
ep.emit('notFound');
}));
ep.once('notFound', function () {
if (!req.session.allowSync) {
return next();
}
var username = (req.session && req.session.username) || 'anonymous';
SyncModuleWorker.sync(name, username, function (err, result) {
if (err) {
return next(err);
}
var pkg = result.pkg && result.pkg.versions[version];
if (!pkg) {
return res.json(404, {
error: 'not exist',
reason: 'version not found: ' + version
});
}
res.json(pkg);
});
});
};
var _downloads = {};
exports.download = function (req, res, next) {
var name = req.params.name;
var filename = req.params.filename;
var cdnurl = nfs.url(common.getCDNKey(name, filename));
res.statusCode = 302;
res.setHeader('Location', cdnurl);
res.end();
_downloads[name] = (_downloads[name] || 0) + 1;
};
setInterval(function () {
// save download count
var totals = [];
for (var name in _downloads) {
var count = _downloads[name];
totals.push([name, count]);
}
_downloads = {};
if (totals.length === 0) {
return;
}
debug('save download total: %j', totals);
var date = utility.YYYYMMDD();
var next = function () {
var item = totals.shift();
if (!item) {
// done
return;
}
DownloadTotal.plusTotal({name: item[0], date: date, count: item[1]}, function (err) {
if (!err) {
return next();
}
logger.error(err);
debug('save download %j error: %s', item, err);
totals.push(item);
// save to _downloads
for (var i = 0; i < totals.length; i++) {
var v = totals[i];
var name = v[0];
_downloads[name] = (_downloads[name] || 0) + v[1];
}
// end
});
};
next();
}, 5000);
exports.upload = function (req, res, next) {
var length = Number(req.headers['content-length']) || 0;
if (!length || req.headers['content-type'] !== 'application/octet-stream') {
return next();
}
var username = req.session.name;
var name = req.params.name;
var id = Number(req.params.rev);
var filename = req.params.filename;
var version = filename.substring(name.length + 1);
version = version.replace(/\.tgz$/, '');
// save version on pkg upload
debug('%s: upload %s, file size: %d', username, req.url, length);
Module.getById(id, function (err, mod) {
if (err || !mod) {
return next(err);
}
var match = mod.package.maintainers.filter(function (item) {
return item.name === username;
});
if (match.length === 0 || mod.name !== name) {
return res.json(403, {
error: 'no_perms',
reason: 'Current user can not publish this module'
});
}
if (mod.version !== 'next') {
// rev wrong
return res.json(403, {
error: 'rev_wrong',
reason: 'rev not match next module'
});
}
var filepath = common.getTarballFilepath(filename);
var ws = fs.createWriteStream(filepath);
var shasum = crypto.createHash('sha1');
req.pipe(ws);
var dataSize = 0;
req.on('data', function (data) {
shasum.update(data);
dataSize += data.length;
});
ws.on('finish', function () {
if (dataSize !== length) {
return res.json(403, {
error: 'size_wrong',
reason: 'Header size ' + length + ' not match download size ' + dataSize,
});
}
shasum = shasum.digest('hex');
var key = common.getCDNKey(name, filename);
nfs.upload(filepath, {key: key, size: length}, function (err, result) {
// remove tmp file whatever
fs.unlink(filepath, utility.noop);
if (err) {
return next(err);
}
var dist = {
tarball: result.url,
shasum: shasum,
size: length
};
mod.package.dist = dist;
mod.package.version = version;
debug('%s module: save file to %s, size: %d, sha1: %s, dist: %j, version: %s',
id, filepath, length, shasum, dist, version);
Module.update(mod, function (err, result) {
if (err) {
return next(err);
}
res.json(201, {ok: true, rev: String(result.id)});
});
});
});
});
};
exports.updateLatest = function (req, res, next) {
var username = req.session.name;
var name = req.params.name;
var version = semver.valid(req.params.version);
if (!version) {
return res.json(400, {
error: 'Params Invalid',
reason: 'Invalid version: ' + req.params.version,
});
}
Module.get(name, 'next', function (err, nextMod) {
if (err) {
return next(err);
}
if (!nextMod) {
return next();
}
var match = nextMod.package.maintainers.filter(function (item) {
return item.name === username;
});
if (match.length === 0) {
return res.json(401, {
error: 'noperms',
reason: 'Current user can not publish this module'
});
}
// check version if not match pkg upload
if (nextMod.package.version !== version) {
return res.json(403, {
error: 'version_wrong',
reason: 'version not match'
});
}
var body = req.body;
nextMod.version = version;
nextMod.author = username;
body.dist = nextMod.package.dist;
body.maintainers = nextMod.package.maintainers;
if (!body.author) {
body.author = {
name: username,
};
}
body._publish_on_cnpm = true;
nextMod.package = body;
debug('update %s:%s %j', nextMod.package.name, nextMod.package.version, nextMod.package.dist);
// change latest to version
Module.update(nextMod, function (err) {
if (err) {
return next(err);
}
// set latest tag
Module.addTag(name, 'latest', version, function (err) {
if (err) {
return next(err);
}
// add a new next version
nextMod.version = 'next';
Module.add(nextMod, function (err, result) {
if (err) {
return next(err);
}
res.json(201, {ok: true, rev: String(result.id)});
});
});
});
});
};
exports.add = function (req, res, next) {
var username = req.session.name;
var name = req.params.name;
var pkg = req.body;
var maintainers = pkg.maintainers || [];
var match = maintainers.filter(function (item) {
return item.name === username;
});
if (match.length === 0) {
return res.json(403, {
error: 'no_perms',
reason: 'Current user can not publish this module'
});
}
var ep = eventproxy.create();
ep.fail(next);
Module.getLatest(name, ep.doneLater('latest'));
Module.get(name, 'next', ep.done(function (nextMod) {
if (nextMod) {
nextMod.exists = true;
return ep.emit('next', nextMod);
}
// ensure next module exits
// because updateLatest will create next module fail
nextMod = {
name: name,
version: 'next',
author: username,
package: {
name: name,
version: 'next',
description: pkg.description,
readme: pkg.readme,
maintainers: pkg.maintainers,
},
};
Module.add(nextMod, ep.done(function (result) {
nextMod.id = result.id;
ep.emit('next', nextMod);
}));
}));
ep.all('latest', 'next', function (latestMod, nextMod) {
var maintainers = latestMod ? latestMod.package.maintainers : nextMod.package.maintainers;
var match = maintainers.filter(function (item) {
return item.name === username;
});
if (match.length === 0) {
return res.json(403, {
error: 'no_perms',
reason: 'Current user can not publish this module'
});
}
if (latestMod || nextMod.exists) {
return res.json(409, {
error: 'conflict',
reason: 'Document update conflict.'
});
}
res.json(201, {
ok: true,
id: name,
rev: String(nextMod.id),
});
});
};
exports.removeWithVersions = function (req, res, next) {
debug('removeWithVersions module %s, with info %j', req.params.name, req.body);
var name = req.params.name;
var username = req.session.name;
var versions = req.body.versions || {};
var ep = eventproxy.create();
ep.fail(next);
Module.listByName(name, ep.doneLater('list'));
ep.once('list', function (mods) {
if (!mods || !mods.length) {
return next();
}
//TODO replace this maintainer check
var match = mods[0].package.maintainers.filter(function (item) {
return item.name === username;
});
if (!match.length || mods[0].name !== name) {
return res.json(403, {
error: 'no_perms',
reason: 'Current user can not update this module'
});
}
var removeVersions = [];
for (var i = 0; i < mods.length; i++) {
var v = mods[i].version;
if (v !== 'next' && !versions[v]) {
removeVersions.push(v);
}
}
if (!removeVersions.length) {
return res.json(201, {ok: true});
}
Module.removeByNameAndVersions(name, removeVersions, ep.done(function () {
res.json(201, {ok: true});
}));
});
};
exports.removeTar = function (req, res, next) {
debug('remove tarball with filename: %s, id: %s', req.params.filename, req.params.rev);
var id = Number(req.params.rev);
var filename = req.params.filename;
var name = req.params.name;
var username = req.session.name;
var ep = eventproxy.create();
ep.fail(next);
Module.getById(id, ep.doneLater('get'));
ep.once('get', function (mod) {
if (!mod) {
return next();
}
//TODO replace this maintainer check
var match = mod.package.maintainers.filter(function (item) {
return item.name === username;
});
if (!match.length || mod.name !== name) {
return res.json(403, {
error: 'no_perms',
reason: 'Current user can not delete this tarball'
});
}
var key = common.getCDNKey(mod.name, filename);
nfs.remove(key, ep.done(function () {
res.json(200, {ok: true});
}));
});
};
exports.removeAll = function (req, res, next) {
debug('remove all the module with name: %s, id: %s', req.params.name, req.params.rev);
var id = Number(req.params.rev);
var name = req.params.name;
var username = req.session.name;
var ep = eventproxy.create();
ep.fail(next);
Module.listByName(name, ep.doneLater('list'));
ep.once('list', function (mods) {
var mod = mods[0];
if (!mod) {
return next();
}
//TODO replace this maintainer check
var match = mod.package.maintainers.filter(function (item) {
return item.name === username;
});
if (!match.length || mod.name !== name) {
return res.json(403, {
error: 'no_perms',
reason: 'Current user can not delete this tarball'
});
}
Total.plusDeleteModule(utility.noop);
Module.removeByName(name, ep.done('remove'));
Module.removeTags(name, ep.done('removeTags'));
});
ep.all('list', 'remove', 'removeTags', function (mods) {
var keys = [];
for (var i = 0; i < mods.length; i++) {
var key = urlparse(mods[i].dist_tarball).path;
key && keys.push(key);
}
var queue = new Bagpipe(5);
keys.forEach(function (key) {
queue.push(nfs.remove, key, function () {
//ignore err here
ep.emit('removeTar');
});
});
ep.after('removeTar', keys.length, function () {
res.json(200, {ok: true});
});
});
};
function parseModsForList(updated, mods, req) {
var results = {
_updated: updated
};
for (var i = 0; i < mods.length; i++) {
var mod = mods[i];
var pkg = {};
try {
pkg = JSON.parse(mod.package);
} catch (e) {
//ignore this pkg
continue;
}
pkg['dist-tags'] = {
latest: pkg.version
};
common.downloadURL(pkg, req);
results[mod.name] = pkg;
}
return results;
}
exports.listAllModules = function (req, res, next) {
var updated = Date.now();
Module.listSince(0, function (err, mods) {
if (err) {
return next(err);
}
return res.json(parseModsForList(updated, mods, req));
});
};
exports.listAllModulesSince = function (req, res, next) {
var query = req.query || {};
if (query.stale !== 'update_after') {
return res.json(400, {
error: 'query_parse_error',
reason: 'Invalid value for `stale`.'
});
}
debug('list all modules from %s', req.startkey);
var startkey = Number(query.startkey) || 0;
var updated = Date.now();
Module.listSince(startkey, function (err, mods) {
if (err) {
return next(err);
}
res.json(parseModsForList(updated, mods, req));
});
};
exports.listAllModuleNames = function (req, res, next) {
Module.listShort(function (err, mods) {
if (err) {
return next(err);
}
var results = mods.map(function (m) {
return m.name;
});
res.json(results);
});
};

View File

@@ -0,0 +1,47 @@
'use strict';
var packageService = require('../../../services/package');
module.exports = deprecateVersions;
/**
* @see https://github.com/cnpm/cnpmjs.org/issues/415
*/
function* deprecateVersions() {
var body = this.request.body;
var name = this.params.name || this.params[0];
var tasks = [];
for (var version in body.versions) {
tasks.push(packageService.getModule(name, version));
}
var rs = yield tasks;
var updateTasks = [];
for (var i = 0; i < rs.length; i++) {
var row = rs[i];
if (!row) {
// some version not exists
this.status = 400;
const error = '[version_error] Some versions: ' + JSON.stringify(Object.keys(body.versions)) + ' not found';
this.body = {
error,
reason: error,
};
return;
}
var data = body.versions[row.package.version];
if (typeof data.deprecated === 'string') {
row.package.deprecated = data.deprecated;
updateTasks.push(packageService.updateModulePackage(row.id, row.package));
}
}
yield updateTasks;
// update last modified
yield packageService.updateModuleLastModified(name);
this.status = 201;
this.body = {
ok: true,
};
}

View File

@@ -0,0 +1,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

@@ -0,0 +1,132 @@
'use strict';
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');
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);
// 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') {
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;
}
this.status = 302;
this.set('Location', url);
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) {
// try to use nsf.url() first
url = url || dist.tarball;
debug('get tarball by 302, url: %s', url);
this.status = 302;
this.set('Location', url);
return;
}
// else use `dist.key` to get tarball from nfs
if (typeof dist.size === 'number' && dist.size > 0) {
this.length = dist.size;
}
this.type = mime.lookup(dist.key);
this.attachment(filename);
this.etag = dist.shasum;
this.body = yield downloadAsReadStream(dist.key);
};
var saving = false;
defer.setInterval(function* () {
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();
for (var i = 0; i < totals.length; i++) {
var item = totals[i];
var name = item[0];
var count = item[1];
try {
yield downloadTotalService.plusModuleTotal({ name: name, date: date, count: count });
} catch (err) {
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);
}
}
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

@@ -0,0 +1,439 @@
'use strict';
var debug = require('debug')('cnpmjs.org:controllers:registry:package:list');
var utility = require('utility');
var packageService = require('../../../services/package');
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() {
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),
];
var modifiedTime = rs[0];
var tags = rs[1];
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
for (var i = 0; i < tags.length; i++) {
var tag = tags[i];
if (tag.gmt_modified > modifiedTime) {
modifiedTime = tag.gmt_modified;
}
}
// must set status first
this.status = 200;
if (this.fresh) {
debug('%s not change at %s, 304 return', name, modifiedTime);
this.status = 304;
return;
}
}
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),
packageService.listMaintainers(name),
];
var rows = r[0];
var starUsers = r[1];
var maintainers = r[2];
debug('show %s got %d rows, %d tags, %d star users, maintainers: %j',
name, rows.length, tags.length, starUsers.length, maintainers);
var starUserMap = {};
for (var i = 0; i < starUsers.length; i++) {
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);
debug('show unpublished %j', unpublishedInfo);
if (unpublishedInfo) {
this.status = 404;
this.jsonp = {
_id: name,
name: name,
time: {
modified: unpublishedInfo.package.time,
unpublished: unpublishedInfo.package,
},
_attachments: {},
};
return;
}
}
// if module not exist in this registry,
// sync the module backend and return package info from official registry
if (rows.length === 0) {
if (!this.allowSync) {
this.status = 404;
const error = '[not_found] document not found';
this.jsonp = {
error,
reason: error,
};
return;
}
// start sync
var logId = yield SyncModuleWorker.sync(name, 'sync-by-install');
debug('start sync %s, get log id %s', name, logId);
return this.redirect(config.officialNpmRegistry + this.url);
}
var latestMod = null;
var readme = null;
// set tags
var distTags = {};
for (var i = 0; i < tags.length; i++) {
var t = tags[i];
distTags[t.tag] = t.version;
}
// set versions and times
var versions = {};
var allVersionString = '';
var times = {};
var attachments = {};
var createdTime = null;
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
var pkg = row.package;
// 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) {
latestMod = row;
readme = pkg.readme;
}
delete pkg.readme;
if (maintainers.length > 0) {
pkg.maintainers = maintainers;
}
if (!createdTime || t < createdTime) {
createdTime = t;
}
}
if (modifiedTime && createdTime) {
var ts = {
modified: modifiedTime,
created: createdTime,
};
for (var t in times) {
ts[t] = times[t];
}
times = ts;
}
if (!latestMod) {
latestMod = rows[0];
}
var rev = String(latestMod.id);
var pkg = latestMod.package;
if (tags.length === 0) {
// some sync error reason, will cause tags missing
// set latest tag at least
distTags.latest = pkg.version;
}
if (!readme && config.enableAbbreviatedMetadata) {
var packageReadme = yield packageService.getPackageReadme(name);
if (packageReadme) {
readme = packageReadme.readme;
}
}
var info = {
_id: name,
_rev: rev,
name: name,
description: pkg.description,
'dist-tags': distTags,
maintainers: pkg.maintainers,
time: times,
users: starUsers,
author: pkg.author,
repository: pkg.repository,
versions: versions,
readme: readme,
_attachments: attachments,
};
info.readmeFilename = pkg.readmeFilename;
info.homepage = pkg.homepage;
info.bugs = pkg.bugs;
info.license = pkg.license;
debug('show module %s: %s, latest: %s', 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

@@ -0,0 +1,16 @@
'use strict';
var packageService = require('../../../services/package');
// GET /-/all
// List all packages names
// https://github.com/npm/npm-registry-client/blob/master/lib/get.js#L86
module.exports = function* () {
var updated = Date.now();
var names = yield packageService.listAllPublicModuleNames();
var result = { _updated: updated };
names.forEach(function (name) {
result[name] = true;
});
this.body = result;
};

View File

@@ -0,0 +1,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

@@ -0,0 +1,32 @@
'use strict';
const packageService = require('../../../services/package');
const config = require('../../../config');
// GET /-/short
// List public all packages names only
module.exports = function* () {
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

@@ -0,0 +1,48 @@
'use strict';
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* 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,
reason: error,
};
return;
}
var startkey = Number(query.startkey);
if (!startkey) {
this.status = 400;
const error = '[query_parse_error] Invalid value for `startkey`.';
this.body = {
error,
reason: error,
};
return;
}
var updated = Date.now();
if (updated - startkey > A_WEEK_MS) {
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 result = { _updated: updated };
names.forEach(function (name) {
result[name] = true;
});
this.body = result;
};

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

@@ -0,0 +1,56 @@
'use strict';
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
module.exports = function* remove(next) {
var name = this.params.name || this.params[0];
var rev = this.params.rev || this.params[1];
debug('remove all the module with name: %s, id: %s', name, rev);
var mods = yield packageService.listModulesByName(name);
debug('removeAll module %s: %d', name, mods.length);
var mod = mods[0];
if (!mod) {
return yield next;
}
yield [
packageService.removeModulesByName(name),
packageService.removeModuleTags(name),
totalService.plusDeleteModule(),
];
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);
}
try {
yield keys.map(function (key) {
return nfs.remove(key);
});
} catch (err) {
logger.error(err);
}
}
// remove the maintainers
yield packageService.removeAllMaintainers(name);
this.body = { ok: true };
};

View File

@@ -0,0 +1,65 @@
'use strict';
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
module.exports = function* removeOneVersion(next) {
var name = this.params.name || this.params[0];
var filename = this.params.filename || this.params[1];
var id = Number(this.params.rev || this.params[2]);
// cnpmjs.org-2.0.0.tgz
var version = filename.split(name + '-')[1];
if (version) {
// 2.0.0.tgz
version = version.substring(0, version.lastIndexOf('.tgz'));
}
if (!version) {
return yield next;
}
debug('remove tarball with filename: %s, version: %s, revert to => rev id: %s', filename, version, id);
if (isNaN(id)) {
return yield next;
}
var rs = yield [
packageService.getModuleById(id),
packageService.getModule(name, version),
];
var revertTo = rs[0];
var mod = rs[1]; // module need to delete
if (!mod || mod.name !== name) {
return yield next;
}
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);
}
}
// remove version from table
yield packageService.removeModulesByNameAndVersions(name, [version]);
debug('removed %s@%s', name, version);
this.body = { ok: true };
};

View File

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

View File

@@ -0,0 +1,81 @@
'use strict';
var debug = require('debug')('cnpmjs.org:controllers:registry:package:show');
var semver = require('semver');
var packageService = require('../../../services/package');
var setDownloadURL = require('../../../lib/common').setDownloadURL;
var SyncModuleWorker = require('../../sync_module_worker');
var config = require('../../../config');
/**
* [deprecate] api
*
* get the special version or tag of a module
*
* GET /:name/:version
* GET /:name/:tag
*/
module.exports = function* show() {
var name = this.params.name || this.params[0];
var tag = this.params.version || this.params[1];
if (tag === '*') {
tag = 'latest';
}
var version = semver.valid(tag);
var range = semver.validRange(tag);
var mod;
if (version) {
mod = yield packageService.getModule(name, version);
} else if (range) {
mod = yield packageService.getModuleByRange(name, range);
} else {
mod = yield packageService.getModuleByTag(name, tag);
}
if (mod) {
setDownloadURL(mod.package, this);
mod.package._cnpm_publish_time = mod.publish_time;
mod.package.publish_time = mod.package.publish_time || mod.publish_time;
var rs = yield [
packageService.listMaintainers(name),
packageService.listModuleTags(name),
];
var maintainers = rs[0];
if (maintainers.length > 0) {
mod.package.maintainers = maintainers;
}
var tags = rs[1];
var distTags = {};
for (var i = 0; i < tags.length; i++) {
var t = tags[i];
distTags[t.tag] = t.version;
}
// show tags for npminstall faster download
mod.package['dist-tags'] = distTags;
this.jsonp = mod.package;
if (config.registryCacheControlHeader) {
this.set('cache-control', config.registryCacheControlHeader);
}
if (config.registryVaryHeader) {
this.set('vary', config.registryVaryHeader);
}
return;
}
// if not fond, sync from source registry
if (!this.allowSync) {
this.status = 404;
const error = '[not_exists] version not found: ' + version;
this.jsonp = {
error,
reason: error,
};
return;
}
// start sync
var logId = yield SyncModuleWorker.sync(name, 'sync-by-install');
debug('start sync %s, get log id %s', name, logId);
this.redirect(config.officialNpmRegistry + this.url);
};

View File

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

View File

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

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

View File

@@ -0,0 +1,140 @@
'use strict';
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
// @see https://github.com/npm/npm-registry-client/commit/effb4bc88d443f764f2c2e8b4dd583cc72cf6084
// PUT /-/user/org.couchdb.user:mk2 { accept: 'application/json',
// 'accept-encoding': 'gzip',
// 'user-agent': 'node/v0.11.12 darwin x64',
// host: '127.0.0.1:7001',
// 'content-type': 'application/json',
// 'content-length': '150',
// connection: 'close' } { name: 'mk2',
// password: '123456',
// email: 'fengmk2@gmail.com',
// _id: 'org.couchdb.user:mk2',
// type: 'user',
// roles: [],
// date: '2014-03-15T02:33:19.465Z' }
// old npm flow
// json:
// PUT /-/user/org.couchdb.user:mk2 { accept: 'application/json',
// 'user-agent': 'node/v0.8.26 darwin x64',
// host: '127.0.0.1:7001',
// 'content-type': 'application/json',
// 'content-length': '258',
// connection: 'keep-alive' }
// { name: 'mk2',
// salt: '12351936478446a5466d4fb1633b80f3838b4caaa03649a885ac722cd6',
// password_sha: '123408912a6db1d96b132a90856d99db029cef3d',
// email: 'fengmk2@gmail.com',
// _id: 'org.couchdb.user:mk2',
// type: 'user',
// roles: [],
// date: '2014-03-15T02:39:25.696Z' }
module.exports = function* addUser() {
var name = this.params.name;
var body = this.request.body || {};
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,
// password_sha: body.password_sha,
email: body.email,
ip: this.ip || '0.0.0.0',
// roles: body.roles || [],
};
ensurePasswordSalt(user, body);
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,
reason: error,
};
return;
}
var existUser = yield userService.get(name);
if (existUser) {
this.status = 409;
const error = '[conflict] User ' + name + ' already exists';
this.body = {
error,
reason: error,
};
return;
}
// add new 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,26 @@
/**!
* cnpmjs.org - controllers/registry/user/common.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var crypto = require('crypto');
var utility = require('utility');
exports.ensurePasswordSalt = function (user, body) {
if (!user.password_sha && body.password) {
// create password_sha on server
user.salt = crypto.randomBytes(30).toString('hex');
user.password_sha = utility.sha1(body.password + user.salt);
}
};

View File

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

View File

@@ -0,0 +1,54 @@
'use strict';
var userService = require('../../../services/user');
// GET /-/user/org.couchdb.user::name
module.exports = function* show(next) {
var name = this.params.name;
var user = yield userService.getAndSave(name);
if (!user) {
return yield next;
}
var data = user.json;
if (!data) {
data = {
_id: 'org.couchdb.user:' + user.name,
_rev: user.rev,
name: user.name,
email: user.email,
type: 'user',
roles: [],
date: user.gmt_modified,
};
}
if (data.login) {
// custom user format
// convert to npm user format
data = {
_id: 'org.couchdb.user:' + user.name,
_rev: user.rev,
name: user.name,
email: user.email,
type: 'user',
roles: [],
date: user.gmt_modified,
avatar: data.avatar_url,
fullname: data.name || data.login,
homepage: data.html_url,
scopes: data.scopes,
site_admin: data.site_admin
};
}
data._cnpm_meta = {
id: user.id,
npm_user: user.isNpmUser,
custom_user: !!data.login,
gmt_create: user.gmt_create,
gmt_modified: user.gmt_modified,
};
this.body = data;
};

View File

@@ -0,0 +1,85 @@
'use strict';
var debug = require('debug')('cnpmjs.org:controllers:registry:user:update');
var ensurePasswordSalt = require('./common').ensurePasswordSalt;
var userService = require('../../../services/user');
// logined before update, no need to auth user again
// { name: 'admin',
// password: '123123',
// email: 'fengmk2@gmail.com',
// _id: 'org.couchdb.user:admin',
// type: 'user',
// roles: [],
// date: '2014-08-05T16:08:22.645Z',
// _rev: '1-1a18c3d73ba42e863523a399ff3304d8',
// _cnpm_meta:
// { id: 14,
// npm_user: false,
// custom_user: false,
// gmt_create: '2014-08-05T15:46:58.000Z',
// gmt_modified: '2014-08-05T15:46:58.000Z',
// admin: true,
// scopes: [ '@cnpm', '@cnpmtest' ] } }
module.exports = function* updateUser(next) {
var name = this.params.name;
var rev = this.params.rev;
if (!name || !rev) {
return yield next;
}
debug('update: %s, rev: %s, user.name: %s', name, rev, this.user.name);
if (name !== this.user.name) {
// must auth user first
this.status = 401;
const error = '[unauthorized] Name is incorrect';
this.body = {
error,
reason: error,
};
return;
}
var body = this.request.body || {};
var user = {
name: body.name,
// salt: body.salt,
// password_sha: body.password_sha,
email: body.email,
ip: this.ip || '0.0.0.0',
rev: body.rev || body._rev,
// roles: body.roles || [],
};
debug('update user %j', body);
ensurePasswordSalt(user, body);
if (!body.password || !user.name || !user.salt || !user.password_sha || !user.email) {
this.status = 422;
const error = '[param_error] params missing, name, email or password missing';
this.body = {
error,
reason: error,
};
return;
}
var result = yield userService.update(user);
if (!result) {
this.status = 409;
const error = '[conflict] Document update conflict';
this.body = {
error,
reason: error,
};
return;
}
this.status = 201;
this.body = {
ok: true,
id: 'org.couchdb.user:' + user.name,
rev: result.rev
};
};

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

@@ -0,0 +1,52 @@
/**!
* cnpmjs.org - controllers/registry/user_package.js
*
* Copyright(c) fengmk2 and other contributors.
* MIT Licensed
*
* Authors:
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
*/
'use strict';
/**
* Module dependencies.
*/
var packageService = require('../../services/package');
// GET /-/by-user/:user
exports.list = function* () {
var users = this.params.user.split('|');
if (users.length > 20) {
this.status = 400;
const error = '[bad_request] reach max user names limit, must <= 20 user names';
this.body = {
error,
reason: error,
};
return;
}
var firstUser = users[0];
if (!firstUser) {
// params.user = '|'
this.body = {};
return;
}
var tasks = {};
for (var i = 0; i < users.length; i++) {
var username = users[i];
tasks[username] = packageService.listPublicModuleNamesByUser(username);
}
var data = yield tasks;
for (var k in data) {
if (data[k].length === 0) {
data[k] = undefined;
}
}
this.body = data;
};

View File

@@ -1,50 +1,77 @@
/**!
* cnpmjs.org - controllers/download.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 Log = require('../proxy/module_log');
var SyncModuleWorker = require('../proxy/sync_module_worker');
var debug = require('debug')('cnpmjs.org:controllers:sync');
var Log = require('../services/module_log');
var SyncModuleWorker = require('./sync_module_worker');
var config = require('../config');
exports.sync = function (req, res, next) {
var username = req.session.name || 'anonymous';
var name = req.params.name;
SyncModuleWorker.sync(name, username, function (err, result) {
if (err) {
return next(err);
}
if (!result.ok) {
return res.json(result.statusCode, result.pkg);
}
res.json(201, {
ok: true,
logId: result.logId
});
});
exports.sync = function* () {
var username = this.user.name || 'anonymous';
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';
var syncUpstreamFirst = this.query.sync_upstream === '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,
reason: error,
};
return;
}
var options = {
type: type,
publish: publish,
noDep: noDep,
syncUpstreamFirst: syncUpstreamFirst,
};
var logId = yield SyncModuleWorker.sync(name, username, options);
debug('sync %s got log id %j', name, logId);
this.status = 201;
this.body = {
ok: true,
logId: logId
};
};
exports.getSyncLog = function (req, res, next) {
var logId = req.params.id;
var name = req.params.name;
var offset = Number(req.query.offset) || 0;
Log.get(logId, function (err, row) {
if (err || !row) {
return next(err);
exports.getSyncLog = function* (next) {
var logId = Number(this.params.id || this.params[1]);
var offset = Number(this.query.offset) || 0;
if (!logId) { // NaN
this.status = 404;
return;
}
var row = yield Log.get(logId);
if (!row) {
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);
}
var log = row.log.trim();
if (offset > 0) {
log = log.split('\n').slice(offset).join('\n');
}
res.json(200, {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,46 +1,60 @@
/**!
* 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 microtime = require('microtime');
var eventproxy = require('eventproxy');
var Total = require('../proxy/total');
var down = require('./download');
var version = require('../package.json').version;
var config = require('../config');
const startTime = '' + Date.now();
let cache = null;
var startTime = '' + microtime.now();
module.exports = function* showTotal() {
if (cache && Date.now() - cache.cache_time < 120000) {
// cache 120 seconds
this.body = cache;
return;
}
exports.show = function (req, res, next) {
var ep = eventproxy.create();
ep.fail(next);
Total.get(ep.done('total'));
down.total(null, ep.done('download'));
ep.all('total', 'download', function (total, download) {
total.download = download;
total.db_name = 'registry';
total.instance_start_time = startTime;
total.node_version = process.version;
total.app_version = version;
total.donate = 'https://me.alipay.com/imk2';
total.sync_model = config.syncModel;
if (req.query.callback) {
return res.jsonp(total, req.query.callback);
const cacheKey = 'registry_total';
if (cacheClient) {
const result = yield cacheClient.get(cacheKey);
if (result) {
this.body = JSON.parse(result);
return;
}
res.json(total);
});
}
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';
total.instance_start_time = startTime;
total.node_version = process.version;
total.app_version = version;
total.donate = 'https://www.gittip.com/fengmk2';
total.sync_model = config.syncModel;
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);
});
}
};

157
controllers/utils.js Normal file
View File

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

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

@@ -0,0 +1,51 @@
'use strict';
var config = require('../../config');
var packageService = require('../../services/package');
var DownloadTotal = require('../../services/download_total');
exports.version = function* () {
var color = 'grey';
var name = this.params[0];
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';
} else if (/^0\./.test(version)) {
// <1.0.0 & >=0.1.0
color = 'green';
} else {
// >=1.0.0
color = 'blue';
}
}
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,162 +0,0 @@
/*!
* cnpmjs.org - controllers/web/package.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
'use strict';
/**
* Module dependencies.
*/
var moment = require('moment');
var eventproxy = require('eventproxy');
var semver = require('semver');
var marked = require('marked');
var gravatar = require('gravatar');
var humanize = require('humanize-number');
var Module = require('../../proxy/module');
var down = require('../download');
var sync = require('../sync');
var Log = require('../../proxy/module_log');
exports.display = function (req, res, next) {
var params = req.params;
var name = params.name;
var tag = params.version;
var ep = eventproxy.create();
ep.fail(next);
if (tag) {
var version = semver.valid(tag);
if (version) {
Module.get(name, version, ep.done('pkg'));
} else {
Module.getByTag(name, tag, ep.done('pkg'));
}
} else {
Module.getByTag(name, 'latest', ep.done('pkg'));
}
down.total(name, ep.done('download'));
ep.all('pkg', 'download', function (pkg, download) {
if (!pkg || !pkg.package) {
return next();
}
pkg.package.fromNow = moment(pkg.publish_time).fromNow();
pkg = pkg.package;
pkg.readme = marked(pkg.readme || '');
if (pkg.maintainers) {
for (var i = 0; i < pkg.maintainers.length; i++) {
var maintainer = pkg.maintainers[i];
if (maintainer.email) {
maintainer.gravatar = gravatar.url(maintainer.email, {s: '50', d: 'retro'}, false);
}
}
}
setLicense(pkg);
for (var k in download) {
download[k] = humanize(download[k]);
}
res.render('package', {
title: 'Package - ' + pkg.name,
package: pkg,
download: download
});
});
};
exports.search = function (req, res, next) {
var params = req.params;
var word = req.params.word;
Module.search(word, function (err, packages) {
if (err) {
return next(err);
}
res.render('search', {
title: 'Keyword - ' + word,
keyword: word,
packages: packages || []
});
});
};
exports.displaySync = function (req, res, next) {
var name = req.params.name;
res.render('sync', {
name: name,
title: 'Sync - ' + name
});
};
function setLicense(pkg) {
var license;
license = pkg.license || pkg.licenses || pkg.licence || pkg.licences;
if (!license) {
return ;
}
if (Array.isArray(license)) {
license = license[0];
}
if (typeof license === 'object') {
pkg.license = {
name: license.name || license.type,
url: license.url
};
}
if (typeof license === 'string') {
if (license.match(/(http|https)(:\/\/)/ig)) {
pkg.license = {
name: license,
url: license
};
} else {
pkg.license = {
url: getOssLicenseUrlFromName(license),
name: license
};
}
}
}
exports.setLicense = setLicense;
function getOssLicenseUrlFromName(name) {
var base = 'http://opensource.org/licenses/';
var licenseMap = {
'bsd': 'BSD-2-Clause',
'mit': 'MIT',
'x11': 'MIT',
'mit/x11': 'MIT',
'apache 2.0': 'Apache-2.0',
'apache2': 'Apache-2.0',
'apache 2': 'Apache-2.0',
'apache-2': 'Apache-2.0',
'apache': 'Apache-2.0',
'gpl': 'GPL-3.0',
'gplv3': 'GPL-3.0',
'gplv2': 'GPL-2.0',
'gpl3': 'GPL-3.0',
'gpl2': 'GPL-2.0',
'lgpl': 'LGPL-2.1',
'lgplv2.1': 'LGPL-2.1',
'lgplv2': 'LGPL-2.1'
};
return licenseMap[name.toLowerCase()] ?
base + licenseMap[name.toLowerCase()] : base + name;
}

View File

@@ -0,0 +1,22 @@
'use strict';
var packageService = require('../../../services/package');
var config = require('../../../config');
module.exports = function* listPrivates() {
var tasks = {};
for (var i = 0; i < config.scopes.length; i++) {
var scope = config.scopes[i];
tasks[scope] = packageService.listPrivateModulesByScope(scope);
}
if (config.privatePackages && config.privatePackages.length > 0) {
tasks['no scoped'] = packageService.listModules(config.privatePackages);
}
var scopes = yield tasks;
yield this.render('private', {
title: 'private packages',
scopes: scopes
});
};

View File

@@ -0,0 +1,51 @@
'use strict';
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, {
limit: limit
});
var match = null;
for (var i = 0; i < result.searchMatchs.length; i++) {
var p = result.searchMatchs[i];
if (p.name === word) {
match = p;
break;
}
}
// return a json result
if (this.query && this.query.type === 'json') {
this.jsonp = {
keyword: word,
match: match,
packages: result.searchMatchs,
keywords: result.keywordMatchs,
};
return;
}
yield this.render('search', {
title: 'Keyword - ' + word,
keyword: word,
match: match,
packages: result.searchMatchs,
keywords: result.keywordMatchs,
});
};

View File

@@ -0,0 +1,34 @@
'use strict';
var packageService = require('../../../services/package');
module.exports = function* searchRange() {
var startKey = this.query.startkey || '';
if (startKey[0] === '"') {
startKey = startKey.substring(1);
}
if (startKey[startKey.length - 1] === '"') {
startKey = startKey.substring(0, startKey.length - 1);
}
var limit = Number(this.query.limit) || 20;
var result = yield packageService.search(startKey, {limit: limit});
var packages = result.searchMatchs.concat(result.keywordMatchs);
var rows = [];
for (var i = 0; i < packages.length; i++) {
var p = packages[i];
var row = {
key: p.name,
count: 1,
value: {
name: p.name,
description: p.description,
}
};
rows.push(row);
}
this.body = {
rows: rows
};
};

View File

@@ -0,0 +1,243 @@
'use strict';
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 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;
// normal: {name: $name, version: $version}
// scope: [$name, $version]
var orginalName = params.name || params[0];
var name = orginalName;
var tag = params.version || params[1];
debug('display %s with %j', name, params);
var getPackageMethod;
var getPackageArgs;
var version = semver.valid(tag || '');
if (version) {
getPackageMethod = 'getModule';
getPackageArgs = [name, version];
} else {
getPackageMethod = 'getModuleByTag';
getPackageArgs = [name, tag || 'latest'];
}
var pkg = yield packageService[getPackageMethod].apply(packageService, getPackageArgs);
if ((!pkg || !pkg.package) && 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);
debug('show unpublished %j', unpublishedInfo);
if (unpublishedInfo) {
var data = {
name: name,
unpublished: unpublishedInfo.package
};
data.unpublished.time = new Date(data.unpublished.time);
if (data.unpublished.maintainers) {
for (var i = 0; i < data.unpublished.maintainers.length; i++) {
var maintainer = data.unpublished.maintainers[i];
if (maintainer.email) {
maintainer.gravatar = gravatar.url(maintainer.email, {s: '50', d: 'retro'}, true);
}
}
}
yield this.render('package_unpublished', {
package: data,
title: 'Package - ' + name
});
return;
}
return yield next;
}
var r = yield [
utils.getDownloadTotal(name),
packageService.listDependents(name),
packageService.listStarUserNames(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.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 || '';
}
if (maintainers.length > 0) {
pkg.maintainers = maintainers;
}
if (pkg.maintainers) {
for (var i = 0; i < pkg.maintainers.length; i++) {
var maintainer = pkg.maintainers[i];
if (maintainer.email) {
maintainer.gravatar = gravatar.url(maintainer.email, {s: '50', d: 'retro'}, true);
}
}
}
if (pkg._npmUser) {
pkg.lastPublishedUser = pkg._npmUser;
if (pkg.lastPublishedUser.email) {
pkg.lastPublishedUser.gravatar = gravatar.url(pkg.lastPublishedUser.email, {s: '50', d: 'retro'}, true);
}
}
if (pkg.repository === 'undefined') {
pkg.repository = null;
}
if (pkg.repository && 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 = {};
}
utils.setLicense(pkg);
for (var k in download) {
download[k] = humanize(download[k]);
}
setDownloadURL(pkg, this, config.registryHost);
pkg.dependents = dependents;
if (pkg.dist) {
pkg.dist.size = bytes(pkg.dist.size || 0);
}
if (pkg.name !== orginalName) {
pkg.name = orginalName;
}
pkg.registryUrl = '//' + config.registryHost + '/' + pkg.name;
pkg.registryPackageUrl = '//' + config.registryHost + '/' + pkg.name + '/' + pkg.version;
// pkg.engines = {
// "python": ">= 0.11.9",
// "node": ">= 0.11.9",
// "node1": ">= 0.8.9",
// "node2": ">= 0.10.9",
// "node3": ">= 0.6.9",
// };
// "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';
}
}
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,
});
};

View File

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

View File

@@ -1,45 +0,0 @@
/*!
* cnpmjs.org - controllers/web/package.js
*
* Copyright(c) cnpmjs.org and other contributors.
* MIT Licensed
*
* Authors:
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
*/
'use strict';
/**
* Module dependencies.
*/
var Module = require('../../proxy/module');
var User = require('../../proxy/user');
var eventproxy = require('eventproxy');
exports.display = function (req, res, next) {
var name = req.params.name;
var ep = eventproxy.create();
ep.fail(next);
Module.listByAuthor(name, ep.done('packages'));
User.get(name, ep.done('user'));
ep.all('packages', 'user', function (packages, user) {
//because of sync, maybe no this user in database,
//but his packages in this registry
if (!user && !packages.length) {
return next();
}
user = {
name: name,
email: user && user.email
};
return res.render('profile', {
title: 'User - ' + name,
packages: packages || [],
user: user
});
});
};

View File

@@ -0,0 +1,57 @@
'use strict';
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;
var isAdmin = common.isAdmin(name);
var scopes = config.scopes || [];
var user;
var r = yield [packageService.listModulesByUser(name), userService.getAndSave(name)];
var packages = r[0];
var user = r[1];
if (!user && !packages.length) {
return yield next;
}
user = user || {};
var data = {
name: name,
email: user.email ? he.encode(user.email, { encodeEverything: true }) : user.email,
json: user.json || {},
isNpmUser: user.isNpmUser,
};
if (data.json.login) {
// custom user format
// convert to npm user format
var json = data.json;
data.json = {
_id: 'org.couchdb.user:' + user.name,
_rev: user.rev,
name: user.name,
email: user.email,
type: 'user',
roles: [],
date: user.gmt_modified,
avatar: json.avatar_url,
fullname: json.name || json.login,
homepage: json.html_url,
im: json.im_url
};
}
yield this.render('profile', {
title: 'User - ' + name,
packages: packages,
user: data,
lastModified: user.gmt_modified,
isAdmin: isAdmin,
scopes: scopes
});
};

View File

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

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

@@ -1,70 +1,197 @@
CREATE TABLE `user` (
CREATE TABLE IF NOT EXISTS `user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`gmt_create` datetime NOT NULL COMMENT 'create time',
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
`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,
`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 `module` (
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(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`description` longtext COMMENT 'module description',
PRIMARY KEY (`id`),
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(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
PRIMARY KEY (`id`),
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(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
PRIMARY KEY (`id`),
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(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
PRIMARY KEY (`id`),
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) NOT NULL COMMENT 'module name',
`author` varchar(100) NOT NULL COMMENT 'module author',
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`version` varchar(30) NOT NULL COMMENT 'module version',
`description` longtext,
`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`);
-- ALTER TABLE `module` CHANGE `package` `package` LONGTEXT CHARACTER SET utf8 COLLATE utf8_general_ci;
-- 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(30) 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 `module_log` (
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',
`username` varchar(100) NOT NULL,
`name` varchar(100) NOT NULL COMMENT 'module name',
`log` longtext,
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`version` varchar(30) 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`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module sync log';
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 `tag` (
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(100) NOT NULL COMMENT 'module name',
`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(30) 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 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 `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(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
`tag` varchar(30) NOT NULL COMMENT 'tag name',
`version` varchar(30) NOT NULL COMMENT 'module version',
`module_id` bigint(20) unsigned NOT NULL COMMENT 'module id',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`, `tag`)
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 `total` (
`name` varchar(100) NOT NULL COMMENT 'total 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(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 `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(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',
@@ -77,7 +204,9 @@ CREATE TABLE `total` (
`last_sync_module` varchar(100) COMMENT 'last sync success module name',
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='total info';
INSERT INTO total(name, gmt_modified, module_delete) VALUES('total', now(), 0, 0);
-- init `total` count
INSERT INTO total(name, gmt_modified) VALUES('total', now())
ON DUPLICATE KEY UPDATE gmt_modified=now();
-- ALTER TABLE `total` ADD `last_sync_time` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'last timestamp sync from official registry'
-- ALTER TABLE `total` ADD `last_exist_sync_time` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'last timestamp sync exist packages from official registry'
-- ALTER TABLE `total` ADD `sync_status` tinyint unsigned NOT NULL DEFAULT '0' COMMENT 'system sync from official registry status'
@@ -86,14 +215,121 @@ INSERT INTO total(name, gmt_modified, module_delete) VALUES('total', now(), 0, 0
-- 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 `download_total` (
-- CREATE TABLE IF NOT EXISTS `download_total` (
-- `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
-- `gmt_create` datetime NOT NULL COMMENT 'create time',
-- `gmt_modified` datetime NOT NULL COMMENT 'modified time',
-- `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 `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(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 `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',
`date` varchar(10) NOT NULL COMMENT 'YYYY-MM-DD format',
`name` varchar(100) NOT NULL COMMENT 'module name',
`count` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'download count',
`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 `date_name` (`date`, `name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module download total info';
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(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 `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';

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

@@ -0,0 +1,261 @@
'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,
};
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);
};

View File

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

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

1038
docs/registry-api.md Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,113 +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\
--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\
--userconfig=$HOME/.lnpmrc'" >> $HOME/.zshrc && source $HOME/.zshrc
```

View File

@@ -1,24 +1,16 @@
# cnpmjs.org: Private npm registry and web for Enterprise
# cnpmjs.org: Private npm registry and web for Company
[![Build Status](https://secure.travis-ci.org/fengmk2/cnpmjs.org.png)](http://travis-ci.org/fengmk2/cnpmjs.org) [![Coverage Status](https://coveralls.io/repos/fengmk2/cnpmjs.org/badge.png)](https://coveralls.io/r/fengmk2/cnpmjs.org) [![Dependency Status](https://gemnasium.com/fengmk2/cnpmjs.org.png)](https://gemnasium.com/fengmk2/cnpmjs.org)
[![NPM](https://nodei.co/npm/cnpmjs.org.png?downloads=true&stars=true)](https://nodei.co/npm/cnpmjs.org/)
## What is this?
> Private npm registry and web for Enterprise, base on MySQL and Simple File Store.
@[JacksonTian](https://github.com/JacksonTian/) had a talk about [private npm](https://speakerdeck.com/jacksontian/qi-ye-ji-node-dot-jskai-fa).
## Install your private npm registry
@see [Install and Get Started](/install).
So `cnpm` is meaning: **Company npm**.
## Registry
* Our public registry: [registry.cnpmjs.org](http://registry.cnpmjs.org)
* Current [cnpmjs.org](/) version: <span id="app-version"></span>
- 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>
@@ -38,12 +30,17 @@
</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>
@@ -58,88 +55,55 @@
</tr>
</tbody>
</table>
</div>
</div>
Running on [Node.js](http://nodejs.org), version <span id="node-version"></span>.
<script src="/js/readme.js"></script>
<script>
$(function () {
function humanize(n, options) {
options = options || {};
var d = options.delimiter || ',';
var s = options.separator || '.';
n = n.toString().split('.');
n[0] = n[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1' + d);
return n.join(s);
}
## Badges
$.getJSON('/total', function (data) {
$('#total-packages').html(humanize(data.doc_count));
$('#total-versions').html(humanize(data.doc_version_count));
$('#total-deletes').html(humanize(data.doc_del_count));
Default style is `flat-square`.
var downloads = $('table.downloads');
downloads.find('td.count:eq(3)').html(humanize(data.download.today));
downloads.find('td.count:eq(4)').html(humanize(data.download.thisweek));
downloads.find('td.count:eq(5)').html(humanize(data.download.thismonth));
downloads.find('td.count:eq(6)').html(humanize(data.download.lastday));
downloads.find('td.count:eq(7)').html(humanize(data.download.lastweek));
downloads.find('td.count:eq(8)').html(humanize(data.download.lastmonth));
### Version
$('#node-version').html(data.node_version || 'v0.10.22');
$('#app-version').html(data.app_version || '0.0.0');
Badge URL: `https://cnpmjs.org/badge/v/cnpmjs.org.svg` ![cnpmjs.org-version-badge](//cnpmjs.org/badge/v/cnpmjs.org.svg)
if (data.sync_model === 'all') {
$('#sync-model').html('This registry will sync all packages from official registry.');
$('#last-sync-time').html(new Date(data.last_sync_time));
} else if (data.sync_model === 'exist') {
$('#sync-model').html('This registry will only update exist packages from official registry.');
$('#last-sync-time').html(new Date(data.last_exist_sync_time));
}
* `<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)
$('#need-sync').html(data.need_sync_num);
$('#success-sync').html(data.success_sync_num);
$('#fail-sync').html(data.fail_sync_num);
$('#left-sync').html(data.left_sync_num);
$('#percent-sync').html(Math.floor(data.success_sync_num / data.need_sync_num * 100));
$('#last-success-name').html('<a target="_blank" href="/package/' + data.last_sync_module + '">' +
data.last_sync_module + '</a>');
### Downloads
if (!data.sync_status) {
$('.syncing').html('');
}
Badge URL: `https://cnpmjs.org/badge/d/cnpmjs.org.svg` ![cnpmjs.org-download-badge](//cnpmjs.org/badge/d/cnpmjs.org.svg)
$('.sync').show();
});
});
</script>
## Usage
## cnpm cli
alias it:
use our npm client [cnpm](https://github.com/cnpm/cnpm)(More suitable with cnpmjs.org and gzip support), you can get our client through npm:
```bash
alias cnpm="npm --registry=http://registry.cnpmjs.org \
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
```
Or you can alias NPM to use it:
```bash
alias cnpm="npm --registry=https://registry.npm.taobao.org \
--cache=$HOME/.npm/.cache/cnpm \
--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://registry.cnpmjs.org \
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=https://registry.npm.taobao.org \
--cache=$HOME/.npm/.cache/cnpm \
--disturl=https://npm.taobao.org/mirrors/node \
--userconfig=$HOME/.cnpmrc"' >> ~/.zshrc && source ~/.zshrc
```
Or you can just use our `cnpm` cli:
```bash
$ npm install cnpm -g
```
### install
Install package from [registry.cnpmjs.org](http://registry.cnpmjs.org). When isntall a package or version not exist, it will try to install from official registry([registry.npmjs.org](http://registry.npmjs.org)), and sync this package to cnpm in the backend.
Install package from [r.cnpmjs.org](//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]
```
@@ -151,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
@@ -166,39 +130,27 @@ $ cnpm publish [name]
$ cnpm unpublish [name]
```
### Orther command
Support all the other npm command.
### Other commands
Support all the other npm commands. e.g.:
```bash
$ cnpm info cnpm
```
## TODO list
@see Github [Issues](https://github.com/fengmk2/cnpmjs.org/issues)
@see Github [Issues](https://github.com/cnpm/cnpmjs.org/issues)
## Authors
## Histories
Release [History](/history).
```bash
$ git summary
## npmjs.org, cnpmjs.org and npm.taobao.org relation
project : cnpmjs.org
repo age : 2 weeks
active : 81 days
commits : 205
files : 83
authors :
120 fengmk2 58.5%
84 dead_horse 41.0%
1 Alsotang 0.5%
```
![npm&cnpm](https://cloud.githubusercontent.com/assets/543405/21505401/fd0b6220-cca1-11e6-86ed-599cc81bb03b.png)
## npm and cnpm relation
## Sponsors
![npm&cnpm](https://docs.google.com/drawings/d/12QeQfGalqjsB77mRnf5Iq5oSXHCIUTvZTwECMonqCmw/pub?w=383&h=284)
## 捐赠 Donate
如果您觉得 [cnpmjs.org] 对您有帮助,欢迎请作者一杯咖啡.
[![Donate](https://img.alipay.com/sys/personalprod/style/mc/btn-index.png)](https://me.alipay.com/imk2)
[cnpmjs.org]: http://cnpmjs.org/
[registry.cnpmjs.org]: http://registry.cnpmjs.org/
- [![阿里云](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)

28
index.js Normal file
View File

@@ -0,0 +1,28 @@
/**!
* 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;
exports.config = config;
exports.startWorker = function (customConfig) {
config.loadConfig(customConfig);
require('./worker');
};
exports.startSync = function (customConfig) {
config.loadConfig(customConfig);
require('./sync');
};

View File

@@ -1,35 +1,94 @@
/**!
* 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 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
var name = filename.replace(/\.tgz$/, '.' + crypto.randomBytes(16).toString('hex') + '.tgz');
// TODO: not only .tgz, and also other extname
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.downloadURL = function (pkg, req) {
if (pkg.dist && pkg.dist.tarball) {
pkg.dist.tarball = 'http://' + req.headers.host + '/' + pkg.name + '/download/' + path.basename(pkg.dist.tarball);
exports.setDownloadURL = function (pkg, ctx, host) {
if (pkg.dist) {
host = host || config.registryHost || ctx.host;
var protocol = config.protocol || ctx.protocol;
pkg.dist.tarball = util.format('%s://%s/%s/download/%s-%s.tgz',
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);
}
}
};
exports.isAdmin = function (username) {
return typeof config.admins[username] === 'string';
};
exports.isMaintainer = function (user, maintainers) {
if (user.isAdmin) {
return true;
}
var username = user.name;
maintainers = maintainers || [];
var match = maintainers.filter(function (item) {
return item.name === username;
});
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,63 +1,93 @@
/*!
* 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';
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');
/**
* Module dependencies.
* Parse the request authorization
* get the real user
*/
var debug = require('debug')('cnpmjs.org:middleware:auth');
var User = require('../proxy/user');
var config = require('../config');
module.exports = function () {
return function* auth(next) {
this.user = {};
module.exports = function (options) {
return function auth(req, res, next) {
req.session.onlySync = config.enablePrivate ? true : false;
if (req.session.name) {
if (config.admins[req.session.name]) {
req.session.isAdmin = true;
}
debug('auth exists user: %s, onlySync: %s, isAdmin: %s, headers: %j',
req.session.name, req.session.onlySync, req.session.isAdmin, req.headers);
return next();
}
var authorization = (req.headers.authorization || '').split(' ')[1] || '';
authorization = authorization.trim();
var authorization = (this.get('authorization') || '').trim();
debug('%s %s with %j', this.method, this.url, authorization);
if (!authorization) {
return next();
return yield unauthorized.call(this, next);
}
authorization = new Buffer(authorization, 'base64').toString().split(':');
var username = authorization[0];
var password = authorization[1];
User.auth(username, password, function (err, row) {
if (err) {
return next(err);
}
var row;
try {
var authorizeType = common.getAuthorizeType(this);
if (!row) {
debug('auth fail user: %j, headers: %j', row, req.headers);
return res.json(401, {
error: 'unauthorized',
reason: 'Name or password is incorrect.'
});
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
this.user.error = err;
}
req.session.name = row.name;
if (config.admins.hasOwnProperty(req.session.name)) {
req.session.isAdmin = true;
}
debug('auth pass user: %j, onlySync: %s, isAdmin: %s, headers: %j',
row, req.session.onlySync, req.session.isAdmin, req.headers);
next();
});
if (!row) {
debug('auth fail user: %j, headers: %j', row, this.header);
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;
};
};
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';
}
}

14
middleware/block.js Normal file
View File

@@ -0,0 +1,14 @@
'use strict';
module.exports = function () {
return function* block(next) {
var ua = String(this.get('user-agent')).toLowerCase();
if (ua === 'ruby') {
this.status = 403;
return this.body = {
message: 'forbidden Ruby user-agent, ip: ' + this.ip
};
}
yield next;
};
};

29
middleware/editable.js Normal file
View File

@@ -0,0 +1,29 @@
'use strict';
var packageService = require('../services/package');
// admin or module's maintainer can modified the module
module.exports = function* editable(next) {
var username = this.user && this.user.name;
var moduleName = this.params.name || this.params[0];
if (username && moduleName) {
if (this.user.isAdmin) {
return yield next;
}
var isMaintainer = yield packageService.isMaintainer(moduleName, username);
if (isMaintainer) {
return yield next;
}
}
this.status = 403;
var message = 'not authorized to modify ' + moduleName;
if (username) {
message = username + ' ' + message;
}
message = '[forbidden] ' + message;
this.body = {
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,
};
};

14
middleware/limit.js Normal file
View File

@@ -0,0 +1,14 @@
'use strict';
var config = require('../config');
var limit = require('koa-limit');
var limitConfig = config.limit;
if (!limitConfig.enable) {
module.exports = function* ignoreLimit(next) {
yield next;
};
} else {
module.exports = limit(limitConfig);
}

View File

@@ -1,25 +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(req, res, next) {
if (!req.session.name) {
return res.json(401, {
error: 'unauthorized',
reason: 'Login first.'
});
module.exports = function *login(next) {
if (this.path === '/-/ping' && this.query.write !== 'true') {
yield next;
return;
}
next();
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,
reason: error,
};
return;
}
if (!this.user.name) {
this.status = 401;
const error = '[unauthorized] Login first';
this.body = {
error,
reason: error,
};
return;
}
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,10 +8,13 @@ var template = '<?xml version="1.0" encoding="UTF-8"?>\
<Url method="get" type="text/html" template="http://${host}/browse/keyword/{searchTerms}"/>\
</OpenSearchDescription>';
var lastModifyDate = new Date();
var config = require('../config');
module.exports = function publishable(req, res, next) {
res.charset = res.charset || 'utf-8';
res.setHeader('Content-Type', 'text/xml');
res.send(template.replace('${host}', req.headers.host));
module.exports = function* opensearch(next) {
if (this.path === '/opensearch.xml') {
this.type = 'text/xml';
this.charset = 'utf-8';
this.body = template.replace('${host}', config.opensearch.host || this.host);
}
yield next;
};

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