Compare commits
447 Commits
2.2.1
...
3.0.0-rc.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee9fc4f349 | ||
|
|
e4c3cd125e | ||
|
|
2ce14d147c | ||
|
|
62fdbd400a | ||
|
|
ee6ba638a5 | ||
|
|
283f14976d | ||
|
|
851ba3fdfa | ||
|
|
2e7354c647 | ||
|
|
5e1c608719 | ||
|
|
596bca908e | ||
|
|
bbae9d3bc3 | ||
|
|
68317cc449 | ||
|
|
d28af7fd7e | ||
|
|
e2582417fa | ||
|
|
99b81f531e | ||
|
|
a1e8a82892 | ||
|
|
14a6f621f8 | ||
|
|
f1217094b7 | ||
|
|
2245dc2967 | ||
|
|
3d34058542 | ||
|
|
a21aed08c5 | ||
|
|
cb2f3de5a6 | ||
|
|
819a499661 | ||
|
|
dc0955320b | ||
|
|
aa7cb3df01 | ||
|
|
35d9f9b00e | ||
|
|
8ddbe9076f | ||
|
|
f0ba573f8b | ||
|
|
134839d31e | ||
|
|
33dd3554f5 | ||
|
|
dffe589e2c | ||
|
|
1c6040bc95 | ||
|
|
7909434b86 | ||
|
|
09ec3f5392 | ||
|
|
39c322332f | ||
|
|
24a0039188 | ||
|
|
b7089d33d4 | ||
|
|
2b74e00cb9 | ||
|
|
d26e9fdff1 | ||
|
|
45f2f8b31f | ||
|
|
90d39aa4fc | ||
|
|
61549b47a2 | ||
|
|
5e9859119f | ||
|
|
e97835f702 | ||
|
|
3cb3fe02f0 | ||
|
|
2cea5cd1c3 | ||
|
|
a8ff647aa0 | ||
|
|
f48f27aae4 | ||
|
|
2c511f2209 | ||
|
|
e7bafb2ee9 | ||
|
|
522ad11124 | ||
|
|
6be1a198c2 | ||
|
|
19563f5851 | ||
|
|
d5ba93ac0d | ||
|
|
92b72169a8 | ||
|
|
4540845046 | ||
|
|
8e2367ee16 | ||
|
|
96369037f9 | ||
|
|
9da2c494f3 | ||
|
|
be05886452 | ||
|
|
f084ebae21 | ||
|
|
dba5c90114 | ||
|
|
ea46399265 | ||
|
|
dd19a606fb | ||
|
|
8d572169b7 | ||
|
|
3ffe576916 | ||
|
|
585f55bbcc | ||
|
|
2cb43c3e33 | ||
|
|
581925db97 | ||
|
|
c385f9d97f | ||
|
|
da2f9640b8 | ||
|
|
754c74ca61 | ||
|
|
b094f5692f | ||
|
|
0c59791e50 | ||
|
|
7f0c141ac2 | ||
|
|
79fb163a3b | ||
|
|
c0ba12960e | ||
|
|
be00b65573 | ||
|
|
316e722a2e | ||
|
|
65bca46f3c | ||
|
|
35b3cef918 | ||
|
|
807187ebeb | ||
|
|
72b5a97cde | ||
|
|
f9d4858862 | ||
|
|
f7e9670025 | ||
|
|
99c4c3fe35 | ||
|
|
444b5d125c | ||
|
|
4b57c118a0 | ||
|
|
d0c3f1b19e | ||
|
|
b395c666be | ||
|
|
03d7215d6c | ||
|
|
8a2f744749 | ||
|
|
e2fc613c98 | ||
|
|
9bdb695375 | ||
|
|
8bd0a2d491 | ||
|
|
769af76c73 | ||
|
|
93d5def8ac | ||
|
|
6a8434e0ca | ||
|
|
a4a25f9e38 | ||
|
|
300e0e676a | ||
|
|
4a3a851256 | ||
|
|
6226ad8050 | ||
|
|
90580a72e5 | ||
|
|
3497bae2b9 | ||
|
|
2073127796 | ||
|
|
d889ebafbd | ||
|
|
ad2d341d2c | ||
|
|
688da2d94b | ||
|
|
25a9030047 | ||
|
|
938a14d0a1 | ||
|
|
3be0a3261e | ||
|
|
017af69cce | ||
|
|
842c0316ed | ||
|
|
82d3ba777f | ||
|
|
9b8491b736 | ||
|
|
364817481c | ||
|
|
fc799304d8 | ||
|
|
68ef6983fb | ||
|
|
3c20267b22 | ||
|
|
26d7147562 | ||
|
|
3141437a42 | ||
|
|
5149aa5a1e | ||
|
|
bd87907b69 | ||
|
|
fc07a38bde | ||
|
|
17f8b6648b | ||
|
|
92acbcedcc | ||
|
|
d1fe6cede7 | ||
|
|
c95cd95593 | ||
|
|
8fbad397f3 | ||
|
|
533c27fa78 | ||
|
|
0121de31a3 | ||
|
|
53343751f7 | ||
|
|
070fd46f36 | ||
|
|
e61c7fa32b | ||
|
|
84a3037d90 | ||
|
|
34d3a1eabe | ||
|
|
1f60a0136c | ||
|
|
6f656a0736 | ||
|
|
e8f66657b4 | ||
|
|
1b30146e94 | ||
|
|
4210b7bdf8 | ||
|
|
5e264beba9 | ||
|
|
56c945740f | ||
|
|
820ae23454 | ||
|
|
bfb29f82c9 | ||
|
|
95aa035a27 | ||
|
|
6c69a38a50 | ||
|
|
43ffa995cb | ||
|
|
6c019de514 | ||
|
|
9a11df5688 | ||
|
|
6b223f887a | ||
|
|
9195e0966b | ||
|
|
47a436afb3 | ||
|
|
ab2d269957 | ||
|
|
ebc84b5a70 | ||
|
|
35a2d4e996 | ||
|
|
73117d826d | ||
|
|
df3c9fa822 | ||
|
|
a0f63fddfb | ||
|
|
1b53463a82 | ||
|
|
fc0fedcadd | ||
|
|
ee79a80fc0 | ||
|
|
ee9bf331c4 | ||
|
|
5b63dbe514 | ||
|
|
32434b22dc | ||
|
|
bb77186bbc | ||
|
|
d52259f2ee | ||
|
|
da70d1ecf8 | ||
|
|
e73b22d6d3 | ||
|
|
10c8a1ecf0 | ||
|
|
9b48106943 | ||
|
|
49bb2a81f5 | ||
|
|
65c0ad8eb8 | ||
|
|
345d6d5dfc | ||
|
|
9656111ef4 | ||
|
|
c9395b983c | ||
|
|
ccd05af9d0 | ||
|
|
0e5586f4f4 | ||
|
|
00fc931291 | ||
|
|
81ce21bfbf | ||
|
|
707bf663e5 | ||
|
|
abfd997d0c | ||
|
|
358b41bc0f | ||
|
|
d984cd9177 | ||
|
|
90b5368a05 | ||
|
|
842bb7fa11 | ||
|
|
f55c6ba1c6 | ||
|
|
cd21e800fa | ||
|
|
8cf3344b68 | ||
|
|
373b535bc5 | ||
|
|
2daa34faab | ||
|
|
10170537dd | ||
|
|
598dc08ef6 | ||
|
|
f0238f6063 | ||
|
|
0d86d05d45 | ||
|
|
1598c2a3d9 | ||
|
|
997e9c5cb5 | ||
|
|
9cfe0502dd | ||
|
|
ff2536fcc5 | ||
|
|
df5564b1ea | ||
|
|
093031e720 | ||
|
|
65b595b899 | ||
|
|
bf5f02908f | ||
|
|
6c3140d422 | ||
|
|
dd27edc36b | ||
|
|
2065c975e0 | ||
|
|
0020a33e65 | ||
|
|
dd423c08d8 | ||
|
|
f9f049c177 | ||
|
|
034bdbb344 | ||
|
|
d7d4cb7fa7 | ||
|
|
b83483e3c0 | ||
|
|
9b909b3233 | ||
|
|
fac9df1de5 | ||
|
|
3262adadd4 | ||
|
|
74e9bdde72 | ||
|
|
f567ef5567 | ||
|
|
c89d7023c4 | ||
|
|
72aa64258f | ||
|
|
b5b63c11f2 | ||
|
|
4da684f3b6 | ||
|
|
fb29419c27 | ||
|
|
8789dadaef | ||
|
|
1c936d4b73 | ||
|
|
7c2f947518 | ||
|
|
bfbfef87d2 | ||
|
|
ea3beceef7 | ||
|
|
b0f0bc0331 | ||
|
|
932547af2a | ||
|
|
79ac9fb823 | ||
|
|
a437eb01eb | ||
|
|
6d921a9c46 | ||
|
|
abcb81d28c | ||
|
|
ea54f32805 | ||
|
|
3dbf29afac | ||
|
|
2c520d97a9 | ||
|
|
38148b24cb | ||
|
|
97895bdfa5 | ||
|
|
0c1c12680c | ||
|
|
6485e4b019 | ||
|
|
ee1e084455 | ||
|
|
5c8d679a6d | ||
|
|
94dc13088c | ||
|
|
9b572d77aa | ||
|
|
c1fa6de038 | ||
|
|
227e1927b4 | ||
|
|
ea98b61ddf | ||
|
|
9d1d60b764 | ||
|
|
7509d909e8 | ||
|
|
9612f64aba | ||
|
|
1b04d59539 | ||
|
|
e6998506f3 | ||
|
|
576397b2b0 | ||
|
|
6c1935bc61 | ||
|
|
1e68cfa568 | ||
|
|
8188d9a07a | ||
|
|
d7ae8d28c1 | ||
|
|
afa5639752 | ||
|
|
e0213e18a0 | ||
|
|
a74d60c7eb | ||
|
|
fcabc49d39 | ||
|
|
ad0722b57b | ||
|
|
6e11a2c8a7 | ||
|
|
0329cfad61 | ||
|
|
ede8a3ce5f | ||
|
|
cde4f2179e | ||
|
|
374dc17a6b | ||
|
|
626259c379 | ||
|
|
36644bda67 | ||
|
|
236e9e2fb3 | ||
|
|
b551216517 | ||
|
|
c8d9d9f075 | ||
|
|
4d43588b10 | ||
|
|
5f71add418 | ||
|
|
3fcfc6306d | ||
|
|
e78c774dbf | ||
|
|
6a54b1328f | ||
|
|
47c8273b7d | ||
|
|
0f01a4c076 | ||
|
|
17b43e3ae4 | ||
|
|
7fd6b10ddf | ||
|
|
7f16050f9e | ||
|
|
f57d525452 | ||
|
|
f05046b8bd | ||
|
|
a34aa7d72f | ||
|
|
72646ffee2 | ||
|
|
060fc65ffb | ||
|
|
4169ee9c6e | ||
|
|
51f380a800 | ||
|
|
c90062e7a0 | ||
|
|
26007f8318 | ||
|
|
0146ce5c30 | ||
|
|
87518df394 | ||
|
|
e49f5474f6 | ||
|
|
af4401fc62 | ||
|
|
c2d608a5cc | ||
|
|
b91b160b66 | ||
|
|
46a3df95bd | ||
|
|
d08aee047e | ||
|
|
406d8cf9a1 | ||
|
|
e1b9ab6b09 | ||
|
|
71f9014777 | ||
|
|
adca5adff2 | ||
|
|
1857f1d0e5 | ||
|
|
2d56820e8b | ||
|
|
119094b230 | ||
|
|
744b6e1e15 | ||
|
|
17f47732a0 | ||
|
|
3a46b97544 | ||
|
|
44976f4afa | ||
|
|
9da7c6ccf2 | ||
|
|
4b68eecf53 | ||
|
|
8d7058b947 | ||
|
|
933ae69d0b | ||
|
|
1af36f0a7a | ||
|
|
f4e9de5094 | ||
|
|
2ccaf404ab | ||
|
|
44c79c7646 | ||
|
|
a845d3f74d | ||
|
|
730113424c | ||
|
|
658be3004f | ||
|
|
214d0ad5c3 | ||
|
|
ca70c8a06d | ||
|
|
1c680367a7 | ||
|
|
3bd43d5ba9 | ||
|
|
0965f8f8e5 | ||
|
|
005eb603ae | ||
|
|
65a9303dff | ||
|
|
3bc6b625ec | ||
|
|
2b0dc6d17f | ||
|
|
842d790dcb | ||
|
|
20eac48349 | ||
|
|
efb80e8c33 | ||
|
|
35fbc651cd | ||
|
|
e55779ffa7 | ||
|
|
878176ea9c | ||
|
|
e3a2eab000 | ||
|
|
7478846754 | ||
|
|
8b244e6c26 | ||
|
|
19217de010 | ||
|
|
93a57b5dae | ||
|
|
defb447f29 | ||
|
|
93b73de164 | ||
|
|
ffc6124c0e | ||
|
|
ab40a992c2 | ||
|
|
5258569044 | ||
|
|
04d8055d56 | ||
|
|
68b6d54f60 | ||
|
|
a950a1e44b | ||
|
|
22f5472dbe | ||
|
|
d6d37a623d | ||
|
|
757184fbc8 | ||
|
|
043fa2c32a | ||
|
|
f94f5ca714 | ||
|
|
788bfc68bc | ||
|
|
dacc39c1d9 | ||
|
|
3fb2228031 | ||
|
|
4535178fa9 | ||
|
|
42e9e1603f | ||
|
|
9a0c3c6c62 | ||
|
|
ba864a62bf | ||
|
|
6dbfe18f58 | ||
|
|
d8c65ff1f3 | ||
|
|
426990f94e | ||
|
|
6d57929e89 | ||
|
|
2b51171592 | ||
|
|
701c9ffa5f | ||
|
|
4260b1c2b6 | ||
|
|
5e926f658d | ||
|
|
78e13bb1d3 | ||
|
|
9e5ec3406d | ||
|
|
21804d277b | ||
|
|
95d1a6b35c | ||
|
|
78395d48b4 | ||
|
|
c6795f201f | ||
|
|
8e7a362bc2 | ||
|
|
7ff3e9d017 | ||
|
|
902b0bb638 | ||
|
|
8097352a6c | ||
|
|
ecf902b73e | ||
|
|
bb0ace7e23 | ||
|
|
d964b11b8c | ||
|
|
53fa67f3bf | ||
|
|
adf7ece3e9 | ||
|
|
02d5b4d16b | ||
|
|
67f8e64140 | ||
|
|
45ceb17238 | ||
|
|
a1ee99618c | ||
|
|
de854a76f1 | ||
|
|
d59ebb44be | ||
|
|
5a2ac54c73 | ||
|
|
8fd8bae5aa | ||
|
|
5929b9b1ea | ||
|
|
aae14f1a6e | ||
|
|
96ad637882 | ||
|
|
73aa9129a8 | ||
|
|
1c89a6f6f9 | ||
|
|
c2beb96dda | ||
|
|
4c4fc3fc7f | ||
|
|
82395fdc33 | ||
|
|
ced4887269 | ||
|
|
8f96a5a57b | ||
|
|
f6e79ff5e7 | ||
|
|
e932f16424 | ||
|
|
622c93c3fe | ||
|
|
ccf3e8a83c | ||
|
|
2069b4334c | ||
|
|
ef84cedd79 | ||
|
|
ba07f87ee0 | ||
|
|
10cbe70378 | ||
|
|
244ce60857 | ||
|
|
97a4f6cd92 | ||
|
|
10197ef2aa | ||
|
|
d2e7350b27 | ||
|
|
9a4a7c6bf0 | ||
|
|
550024176f | ||
|
|
90485d33dd | ||
|
|
02d9bd923e | ||
|
|
a256e044c5 | ||
|
|
a194fc8f3c | ||
|
|
0f61c71e2b | ||
|
|
2acfc52057 | ||
|
|
e144506634 | ||
|
|
cc9b547c86 | ||
|
|
e8bea69a4d | ||
|
|
cd36984a56 | ||
|
|
ec00eeee75 | ||
|
|
571aea5277 | ||
|
|
9e9fd84e12 | ||
|
|
dbe0f96855 | ||
|
|
66f17619e6 | ||
|
|
1d491a244b | ||
|
|
bbb6a59441 | ||
|
|
76e0563308 | ||
|
|
5e969cc305 | ||
|
|
9d886b0599 | ||
|
|
cd2e96f460 | ||
|
|
f86734674d | ||
|
|
e2c37d312f | ||
|
|
689e7e1c08 | ||
|
|
cd6f78d91b | ||
|
|
b832c91d21 | ||
|
|
646c830a68 | ||
|
|
e543f8578b | ||
|
|
5ff7e2867f | ||
|
|
98e8e7d498 | ||
|
|
b7eb9f8374 |
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
52
.github/workflows/nodejs.yml
vendored
Normal file
52
.github/workflows/nodejs.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: Node.js CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '0 2 * * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: cnpmjs_test
|
||||
ports:
|
||||
- 3306:3306
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: [10, 12]
|
||||
os: [ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- name: Checkout Git Source
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm i -g npminstall && npminstall
|
||||
|
||||
- name: Continuous Integration
|
||||
run: npm run ci
|
||||
|
||||
- name: Code Coverage
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -29,3 +29,4 @@ coverage/
|
||||
config/web_readme.md
|
||||
.tmp/
|
||||
*.sqlite
|
||||
|
||||
|
||||
11
.travis.yml
11
.travis.yml
@@ -1,11 +0,0 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- '1'
|
||||
- '2'
|
||||
- '4'
|
||||
addons:
|
||||
- postgresql: '9.3'
|
||||
script: 'make test-travis-all'
|
||||
after_script:
|
||||
- "npm i codecov.io && cat ./coverage/coverage.json | ./node_modules/codecov.io/bin/codecov.io.js"
|
||||
5
AUTHORS
5
AUTHORS
@@ -5,3 +5,8 @@ dead-horse <dead_horse@qq.com> (https://github.com/dead-horse)
|
||||
alsotang <alsotang@gmail.com> (https://github.com/alsotang)
|
||||
4simple <wondger@qq.com> (https://github.com/4simple)
|
||||
afc163 <afc163@gmail.com> (https://github.com/afc163)
|
||||
Yuwei Ba <i@xiaoba.me> (https://github.com/ibigbug)
|
||||
dickeylth <dickeylth@gmail.com> (https://github.com/dickeylth)
|
||||
wenbing <wenbing@users.noreply.github.com> (https://github.com/wenbing)
|
||||
21paradox <1036339815@qq.com> (https://github.com/21paradox)
|
||||
|
||||
|
||||
28
Dockerfile
Normal file
28
Dockerfile
Normal 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"]
|
||||
|
||||
496
History.md
496
History.md
@@ -1,4 +1,494 @@
|
||||
|
||||
3.0.0-rc.50 / 2021-11-04
|
||||
==================
|
||||
|
||||
**features**
|
||||
* [[`e4c3cd1`](http://github.com/cnpm/cnpmjs.org/commit/e4c3cd125ebe771ecc31b4a207ab926c13817f16)] - feat: package view add download total count (#1673) (Solais <<924615994@qq.com>>)
|
||||
* [[`283f149`](http://github.com/cnpm/cnpmjs.org/commit/283f14976dae8ce9144d387533d1637a79c6cf12)] - feat: add missing abbreviated meta attibutes (#1668) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
|
||||
**fixes**
|
||||
* [[`62fdbd4`](http://github.com/cnpm/cnpmjs.org/commit/62fdbd400a2259983c6793173ac8d816a732452c)] - fix: should update etag after abbreviated meta change (#1670) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`2e7354c`](http://github.com/cnpm/cnpmjs.org/commit/2e7354c647d78a41f033f2066c6eb60e270f2a6b)] - fix: allow download without scope filename (#1665) (killa <<killa123@126.com>>)
|
||||
|
||||
3.0.0-rc.46 / 2021-09-10
|
||||
==================
|
||||
|
||||
**fixes**
|
||||
* [[`596bca9`](http://github.com/cnpm/cnpmjs.org/commit/596bca908ee87504dfeb3dbf463b4d9a551d9f27)] - fix: fs-cnpm on dockerize config (#1656) (wxhuang <<wxhuang1985@gmail.com>>)
|
||||
* [[`bbae9d3`](http://github.com/cnpm/cnpmjs.org/commit/bbae9d3bc3a565a59c397a63c4301272ac4509e9)] - fix: new publish with token should add user to maintainers (#1662) (killa <<killa123@126.com>>)
|
||||
|
||||
3.0.0-rc.45 / 2021-07-28
|
||||
==================
|
||||
|
||||
**features**
|
||||
* [[`d28af7f`](http://github.com/cnpm/cnpmjs.org/commit/d28af7fd7e1bf530ad2e5a73fa5a54d2caf5cf9b)] - feat: support custom tokenService (#1658) (killa <<killa123@126.com>>)
|
||||
|
||||
**others**
|
||||
* [[`e258241`](http://github.com/cnpm/cnpmjs.org/commit/e2582417fa06a82c2fbb4d08eaf764465683a00b)] - remove maintainers logic (#1654) (Solais <<924615994@qq.com>>)
|
||||
3.0.0-rc.44 / 2021-06-11
|
||||
==================
|
||||
|
||||
**features**
|
||||
* [[`a1e8a82`](https://github.com/cnpm/cnpmjs.org/commit/a1e8a8289276275b995d15b3c254fbdbace6dbae)] - feat: impl npm owner hooks (#1645) (killa <<killa123@126.com>>)
|
||||
|
||||
3.0.0-rc.43 / 2021-05-36
|
||||
==================
|
||||
|
||||
**features**
|
||||
* [[`a21aed0`](https://github.com/cnpm/cnpmjs.org/commit/a21aed08c5fe1ea09f4fda157ac3c12bd609781d)] - feat: impl sync to/from backup files (#1612) (killa <<killa123@126.com>>)
|
||||
|
||||
**fixes**
|
||||
* [[`2245dc2`](https://github.com/cnpm/cnpmjs.org/commit/2245dc2967ec070b8bcc618ebfad0cd4cd297fb8)] - feat: impl accelerate request (#1637) (killa <<killa123@126.com>>)
|
||||
|
||||
3.0.0-rc.42 / 2021-04-30
|
||||
==================
|
||||
|
||||
**features**
|
||||
* [[`a21aed0`](https://github.com/cnpm/cnpmjs.org/commit/a21aed08c5fe1ea09f4fda157ac3c12bd609781d)] - feat: impl sync to/from backup files (#1612) (killa <<killa123@126.com>>)
|
||||
|
||||
3.0.0-rc.39 / 2021-01-14
|
||||
==================
|
||||
|
||||
**features**
|
||||
* [[`33dd355`](http://github.com/cnpm/cnpmjs.org/commit/33dd3554f5daf13de33f04128be6853ce120636f)] - feat: impl dist-tag hooks (#1612) (killa <<killa123@126.com>>)
|
||||
|
||||
3.0.0-rc.38 / 2021-01-12
|
||||
==================
|
||||
|
||||
**features**
|
||||
* [[`c6040b`](http://github.com/cnpm/cnpmjs.org/commit/1c6040bc95610e23b4756baa09e8119cda2fe01e)] - performance: optimise query pkg latestModified (#1611) (killa <<killa123@126.com>>)
|
||||
|
||||
|
||||
3.0.0-rc.37 / 2020-10-21
|
||||
==================
|
||||
|
||||
**features**
|
||||
* [[`39c3223`](http://github.com/cnpm/cnpmjs.org/commit/39c322332ffafc512bf56c1679d2904fece2ae07)] - feat: new registry api (#1597) (killa <<killa123@126.com>>)
|
||||
* [[`45f2f8b`](http://github.com/cnpm/cnpmjs.org/commit/45f2f8b31f095eeadf0f47e234d6eb225e6b197f)] - feat: impl registry token api (#1590) (killa <<killa123@126.com>>)
|
||||
* [[`e97835f`](http://github.com/cnpm/cnpmjs.org/commit/e97835f7020e945e59fa7a84b14ab58c580add1e)] - feat: support custom web middlewares (#1563) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`3cb3fe0`](http://github.com/cnpm/cnpmjs.org/commit/3cb3fe02f01dd669ad4bd3aebca51c44eb9e5938)] - feat: list all package versions by date (#1557) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`a8ff647`](http://github.com/cnpm/cnpmjs.org/commit/a8ff647aa0f73076f4625e395e5da8ced9f61680)] - feat: retry sync fail on cnpm registry (#1547) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`2c511f2`](http://github.com/cnpm/cnpmjs.org/commit/2c511f2209329e95b0cbe7603fa98a7f93c66474)] - feat: add unpublishRemoveTarball mode (#1536) (Khaidi Chu <<i@2333.moe>>)
|
||||
* [[`19563f5`](http://github.com/cnpm/cnpmjs.org/commit/19563f58517ffaebed8006630bd467f15b71d9ff)] - feat: allow to disable npm audits proxy (#1430) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`8e2367e`](http://github.com/cnpm/cnpmjs.org/commit/8e2367ee1676bd36a4112cf0f6dce2c4f422806e)] - feat: dont check db data on tgz download request (#1477) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`be05886`](http://github.com/cnpm/cnpmjs.org/commit/be05886452803d46f614bdde497ccdec8e9ed734)] - feat: add vary header on cdn (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`ea46399`](http://github.com/cnpm/cnpmjs.org/commit/ea46399265615c70ee33d9cab9ba5d5ce312fb67)] - feat: allow disable search page (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`581925d`](http://github.com/cnpm/cnpmjs.org/commit/581925db9733d295be02e75f0090db05fd6bae75)] - feat: support cache-control header on registry request (#1468) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`7f0c141`](http://github.com/cnpm/cnpmjs.org/commit/7f0c141ac2f7679b5322aadd537c5ff1bef0b032)] - feat: allow config request protocol (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`807187e`](http://github.com/cnpm/cnpmjs.org/commit/807187ebeb0b266828a59724234e1a99c3238eb3)] - feat: add redis cache to import list all versions api perf (#1441) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`99c4c3f`](http://github.com/cnpm/cnpmjs.org/commit/99c4c3fe35a9fde805751ef3d44413413f053f45)] - feat: support customized middlewares (#1436) (Khaidi Chu <<i@2333.moe>>)
|
||||
* [[`4b57c11`](http://github.com/cnpm/cnpmjs.org/commit/4b57c118a0b044f41b1c98eaf92449221c984c15)] - feat: can override tgz download options (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`b395c66`](http://github.com/cnpm/cnpmjs.org/commit/b395c666be3ae6b237803239fae8678647f3b70b)] - feat: proxy npm audit request (#1419) (alsotang <<alsotang@gmail.com>>)
|
||||
* [[`a4a25f9`](http://github.com/cnpm/cnpmjs.org/commit/a4a25f9e381aa20e1ef1e709f320aae41f3ae466)] - feat: use faster etag instead of koa-etag (#1409) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`90580a7`](http://github.com/cnpm/cnpmjs.org/commit/90580a72e56c69f8f03bbdb64d79b4b1b139fbbf)] - feat: configurable view directory (#1400) (Khaidi Chu <<i@2333.moe>>)
|
||||
* [[`ad2d341`](http://github.com/cnpm/cnpmjs.org/commit/ad2d341d2c9317b062a25363f4805aedfef3913b)] - feat: sync downloads total <= 10000 unpublish package (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`25a9030`](http://github.com/cnpm/cnpmjs.org/commit/25a90300473e6ac437e393de139cebde1e354e8c)] - feat: allow to close mysql trace (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`017af69`](http://github.com/cnpm/cnpmjs.org/commit/017af69cce23c870694d124f4a865864e5c061cd)] - feat: add badgeService define on config (#1387) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`842c031`](http://github.com/cnpm/cnpmjs.org/commit/842c0316ede2b19b76d9c1ca790902de467c82e9)] - feat: show versions list on package page (#1386) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`bd87907`](http://github.com/cnpm/cnpmjs.org/commit/bd87907b69d3e65aa544930b5c7f04e75bdbc773)] - feat: auto retry if download tgz error (#1363) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`533c27f`](http://github.com/cnpm/cnpmjs.org/commit/533c27fa78323ee50fcd549115034915ea3017ef)] - feat: support nfs.url return multi urls (#1344) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`e61c7fa`](http://github.com/cnpm/cnpmjs.org/commit/e61c7fa32bdc54ef4474a071da686c70e512b009)] - feat: support pass through querystring to tgz url (#1334) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`34d3a1e`](http://github.com/cnpm/cnpmjs.org/commit/34d3a1eabe927dc5c8c87436e2b644c70a7abc2a)] - feat: auto sync delete packages which deleted in 24 hours (#1315) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`4210b7b`](http://github.com/cnpm/cnpmjs.org/commit/4210b7bdf8bfe8dfa2578802fd1d14e7411d4ea6)] - feat: can config to not sync deleted versions (#1282) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`56c9457`](http://github.com/cnpm/cnpmjs.org/commit/56c945740f545abe9ba55759f6b1502a3abc453d)] - feat: let opensearch host can be config (#1258) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
|
||||
**fixes**
|
||||
* [[`b7089d3`](http://github.com/cnpm/cnpmjs.org/commit/b7089d33d400f9fd4fc398479d4dac5aab26b633)] - fix: set maintainer to current user if maintainer is undefined (#1592) (killa <<killa123@126.com>>)
|
||||
* [[`2b74e00`](http://github.com/cnpm/cnpmjs.org/commit/2b74e00cb9ae20e9cf2f06c54ef8dbe6a36b4066)] - fix: release 3.0.0-rc.35 fix npm include functions dir (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`61549b4`](http://github.com/cnpm/cnpmjs.org/commit/61549b47a2f49c163bef6994f1e0f5f761317975)] - fix: avoid "ENAMETOOLONG: name too long" error (#1583) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`e7bafb2`](http://github.com/cnpm/cnpmjs.org/commit/e7bafb2ee9d80ce3ef4087a6b69bc17517f85ec5)] - fix: audit proxy test cases (#1537) (Khaidi Chu <<i@2333.moe>>)
|
||||
* [[`92b7216`](http://github.com/cnpm/cnpmjs.org/commit/92b72169a89cec333177d1ba65205a31e60ebbb2)] - fix: maintainer permission greater than scope (#1494) (Khaidi Chu <<i@2333.moe>>)
|
||||
* [[`f084eba`](http://github.com/cnpm/cnpmjs.org/commit/f084ebae2106c8d4435dc0385e493fe18c6cec8a)] - fix: cpu usage 100% in node@6.x (#1470) (Yiman Liu <<413893093@qq.com>>)
|
||||
* [[`8d57216`](http://github.com/cnpm/cnpmjs.org/commit/8d572169b7293a33035257d3525c66b0abb5b679)] - fix: add cache on total (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`585f55b`](http://github.com/cnpm/cnpmjs.org/commit/585f55bbcc0ce257bfab2f0f545dd8a89c66ca49)] - fix: download url pathname (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`da2f964`](http://github.com/cnpm/cnpmjs.org/commit/da2f9640b87f1b110210b7b8caaf26b4b854ede8)] - fix: dont override exists weburl (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`b094f56`](http://github.com/cnpm/cnpmjs.org/commit/b094f5692f83700f152dd6ea9eb65f67385f6b5f)] - fix: changes stream syncer without deps (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`65bca46`](http://github.com/cnpm/cnpmjs.org/commit/65bca46f3c275bac5dc7497eb266d84605f6f8f8)] - fix: don't cache npm_service.cnpmjs.org request (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`f9d4858`](http://github.com/cnpm/cnpmjs.org/commit/f9d4858862a4b70cb989c3c60478c2424ca2c139)] - fix: avoid toString as downloads count key (#1438) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`8a2f744`](http://github.com/cnpm/cnpmjs.org/commit/8a2f744749fc9f1297ff298fafe14deacf67efea)] - fix: don't update __all__ downloads every times (#1417) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`9bdb695`](http://github.com/cnpm/cnpmjs.org/commit/9bdb695375a800464636d70981f433b7a11dd82d)] - fix: proxy to source registry when package is public scoped with encoded path (#1415) (Albert Zhang <<label4king@163.com>>)
|
||||
* [[`8bd0a2d`](http://github.com/cnpm/cnpmjs.org/commit/8bd0a2d49195734afa988cce69804d8540bbda19)] - fix: swap compress middleware and notFound position (#1413) (alsotang <<alsotang@gmail.com>>)
|
||||
* [[`93d5def`](http://github.com/cnpm/cnpmjs.org/commit/93d5def8ac8882edbd526e5a7341e07c99463b25)] - fix: show package when non-semver version of semver tag (#1411) (Khaidi Chu <<i@2333.moe>>)
|
||||
* [[`6a8434e`](http://github.com/cnpm/cnpmjs.org/commit/6a8434e0cae391981579af1a0b533aff0008904f)] - fix: Don't display sync info when the sync mode is none (#1410) (XingKai Zhang <<jack_zhxk@163.com>>)
|
||||
* [[`4a3a851`](http://github.com/cnpm/cnpmjs.org/commit/4a3a851256483d438753b154d80d28c12c1d625c)] - fix: use <%- instead of <%= in user profile page (#1404) (Khaidi Chu <<i@2333.moe>>)
|
||||
* [[`3497bae`](http://github.com/cnpm/cnpmjs.org/commit/3497bae2b94237664716911de965a4b27afc083a)] - fix: Obfuscate email address (#1391) (Ankur Kumar <<ankurk91@users.noreply.github.com>>)
|
||||
* [[`9b8491b`](http://github.com/cnpm/cnpmjs.org/commit/9b8491b736ebcb98df02d26c41334cf7fce306dc)] - fix: use https://cdn.staticfile.org (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`fc79930`](http://github.com/cnpm/cnpmjs.org/commit/fc799304d8c6710e71364bdf1d1ed0961b9e8695)] - fix: should return `[done] Sync {name}` string when task finished (#1382) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`3c20267`](http://github.com/cnpm/cnpmjs.org/commit/3c20267b22491cd2ac2d751ccc459cf1f4fb0f1f)] - fix: don't retry to save log when db error (#1381) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`5149aa5`](http://github.com/cnpm/cnpmjs.org/commit/5149aa5a1eb01dfc17f8de1cb6c6abfecca0ed96)] - fix: proxy public package from source registry (#1375) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`fc07a38`](http://github.com/cnpm/cnpmjs.org/commit/fc07a38bde81bd93ef9067f3aacb06ae8e76e12b)] - fix: make sure replicate pkg is the latest pkg (#1347) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`17f8b66`](http://github.com/cnpm/cnpmjs.org/commit/17f8b6648b2cf8cb4cf17daef2a2477f74a671e8)] - fix: retry from registry when no_db_file error on replicate (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`d1fe6ce`](http://github.com/cnpm/cnpmjs.org/commit/d1fe6cede7b5a082eabfe9eb94225c9af9399e62)] - fix: add other_urls on download dist tarball (#1345) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`8fbad39`](http://github.com/cnpm/cnpmjs.org/commit/8fbad397f3ab7177c6e6c9b458b4b0bf3d24fbd7)] - fix: use rimraf instead of fs.unlink (#1338) (Yiyu He <<dead_horse@qq.com>>)
|
||||
* [[`0121de3`](http://github.com/cnpm/cnpmjs.org/commit/0121de31a3b7a8da38e31fca4e10d973c07d79e7)] - fix: no need to resync again (#1336) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`84a3037`](http://github.com/cnpm/cnpmjs.org/commit/84a3037d90d4b3a316752eda7440ff5c73b0872f)] - fix: avoid query too frequently (#1329) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`1f60a01`](http://github.com/cnpm/cnpmjs.org/commit/1f60a0136c5f2e4a33827d1f36b38c49e1e3dec6)] - fix: replicate request error, try to request from official registry (#1316) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`6f656a0`](http://github.com/cnpm/cnpmjs.org/commit/6f656a0736c7d1d8b58288ff97590d7cb1317ecd)] - fix: save sync last time when successes > 1000 (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`1b30146`](http://github.com/cnpm/cnpmjs.org/commit/1b30146e94e7e72f9e762947b1ecdbd176d64532)] - fix: npm >= v5.5.0 login need not `email` (#1275) (#1304) (wmzy <<1256573276@qq.com>>)
|
||||
* [[`820ae23`](http://github.com/cnpm/cnpmjs.org/commit/820ae23454f0f9755456681f3ced03e634cb3109)] - fix: control sync frequency (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`bfb29f8`](http://github.com/cnpm/cnpmjs.org/commit/bfb29f82c967cb68f4de3a314200d95a8c59baff)] - fix: use _npmUser reset the maintainers (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`95aa035`](http://github.com/cnpm/cnpmjs.org/commit/95aa035a275089b50dfc2590497e3bc7319f4f6b)] - fix: make sure maintainers exists on sync worker (liang feng <<anhulife@gmail.com>>)
|
||||
* [[`6c69a38`](http://github.com/cnpm/cnpmjs.org/commit/6c69a38a508812f0320866d70b555de02e1fc204)] - fix: if replicate error, retry from official registry (#1230) (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`43ffa99`](http://github.com/cnpm/cnpmjs.org/commit/43ffa995cb8a724e8cd04224c2f137d407bfe014)] - fix: "start" should wait for "stop" to remove the pid file(using Promise) (#1220) (cloudstone <<baby31529@gmail.com>>)
|
||||
* [[`6c019de`](http://github.com/cnpm/cnpmjs.org/commit/6c019de514c9f4a62db1a1814ca2359408609074)] - fix: changes_stream_syncer log url should not contain sync_upstream=true (fengmk2 <<fengmk2@gmail.com>>)
|
||||
|
||||
**others**
|
||||
* [[`522ad11`](http://github.com/cnpm/cnpmjs.org/commit/522ad11124f168788b28dd925417ae37eb9d3991)] - update readme for now situation (#1506) (alsotang <<alsotang@gmail.com>>)
|
||||
* [[`0c59791`](http://github.com/cnpm/cnpmjs.org/commit/0c59791e50ef9d3080d5a2ab3e24b5899bd91446)] - Release Release 3.0.0-rc.19 (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`79fb163`](http://github.com/cnpm/cnpmjs.org/commit/79fb163a3b12f1b9c4c9eafad7f2041e7c4c4dbf)] - chore: README fix typo ( not to use plural for code ) (#1448) (Paul Verest <<enide.github@gmail.com>>)
|
||||
* [[`be00b65`](http://github.com/cnpm/cnpmjs.org/commit/be00b6557359d328c851e538827d6c681c2c3416)] - refactor: add detail message to error and keep reason (#1445) (alsotang <<alsotang@gmail.com>>)
|
||||
* [[`f7e9670`](http://github.com/cnpm/cnpmjs.org/commit/f7e9670025c6e7f09d8aa88c676938a2cf4849b5)] - Release Release 3.0.0-rc.14 (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`d0c3f1b`](http://github.com/cnpm/cnpmjs.org/commit/d0c3f1b19e46e73ce389e78413304a1542811b5f)] - test: shouldjs change from getter to function call (#1420) (alsotang <<alsotang@gmail.com>>)
|
||||
* [[`d889eba`](http://github.com/cnpm/cnpmjs.org/commit/d889ebafbd6ff1bc15fbf277fd8e143a57e6cac6)] - deps: use agentkeepalive@4 (fengmk2 <<fengmk2@gmail.com>>)
|
||||
* [[`938a14d`](http://github.com/cnpm/cnpmjs.org/commit/938a14d0a13b711c7b91d795151a7266b0a43c5a)] - chore: Hall of Fame integration on README (#1388) (Gwenael Pluchon <<gwenael.pluchon+github@gmail.com>>)
|
||||
* [[`26d7147`](http://github.com/cnpm/cnpmjs.org/commit/26d7147562a1ae21db8bfec26983daf311353d96)] - refactor: normalize database structure (#1376) (Khaidi Chu <<i@2333.moe>>)
|
||||
* [[`5334375`](http://github.com/cnpm/cnpmjs.org/commit/53343751f7c0a34ea0a346172bff0818d27864dd)] - chore: add latest-3 tag (fengmk2 <<fengmk2@gmail.com>>)
|
||||
|
||||
3.0.0-alpha.8 / 2017-06-15
|
||||
==================
|
||||
|
||||
* fix: should remove unpublished version on ModuleAbbreviated too (#1192)
|
||||
* docs: Dockerized cnpmjs.org configuration with installation guide (#1191)
|
||||
|
||||
3.0.0-alpha.7 / 2017-06-01
|
||||
==================
|
||||
|
||||
* fix: add missing publish_time property on package list api (#1185)
|
||||
|
||||
3.0.0-alpha.6 / 2017-05-18
|
||||
==================
|
||||
|
||||
* feat: add globalHook on config (#1177)
|
||||
* fix: TypeError caused by invalid engines property (#1151)
|
||||
* test: add new test for application/vnd.npm.install-v1+json request
|
||||
|
||||
3.0.0-alpha.5 / 2017-04-14
|
||||
==================
|
||||
|
||||
* fix: should auto sync missing deprecated property (#1167)
|
||||
|
||||
3.0.0-alpha.4 / 2017-04-12
|
||||
==================
|
||||
|
||||
* fix: add missing deprecated on abbreviated meta (#1165)
|
||||
|
||||
3.0.0-alpha.2 / 2017-03-27
|
||||
==================
|
||||
|
||||
* fix: only get from package_readme table
|
||||
|
||||
3.0.0-alpha.1 / 2017-03-27
|
||||
==================
|
||||
|
||||
* chore: start 3.x
|
||||
* fix: ignore sync npm registry status 502
|
||||
* feat: remove readme from package
|
||||
* feat: [BREAKING_CHANGE] support abbreviated meta
|
||||
|
||||
2.19.4 / 2017-03-26
|
||||
==================
|
||||
|
||||
* feat: need to sync sourceNpmRegistry also (#1153)
|
||||
* docs: change user.json to utf8mb4
|
||||
|
||||
2.19.3 / 2017-02-22
|
||||
==================
|
||||
|
||||
* fix: should get package from orginal registry when package is unpublished (#1130)
|
||||
|
||||
2.19.2 / 2017-01-05
|
||||
==================
|
||||
|
||||
* fix: should auto sync un-deprecate message (#1105)
|
||||
|
||||
2.19.1 / 2016-12-29
|
||||
==================
|
||||
|
||||
* fix: try to use the best repository url (#1102)
|
||||
|
||||
2.19.0 / 2016-12-21
|
||||
==================
|
||||
|
||||
* feat: keyword search with limit to support keywords > 100 (#1097)
|
||||
|
||||
2.18.0 / 2016-12-05
|
||||
==================
|
||||
|
||||
* fix: support downloads total on scope package (#1088)
|
||||
* fix: try to sync from official replicate (#1076)
|
||||
* feat: add change password script (#1070)
|
||||
* test: skip always fail tests
|
||||
* test: add node v7
|
||||
* feat: show more sync info
|
||||
|
||||
2.17.2 / 2016-11-13
|
||||
==================
|
||||
|
||||
* fix: ignore long package name on unpublished sync (#1067)
|
||||
|
||||
2.17.1 / 2016-11-08
|
||||
==================
|
||||
|
||||
* fix: add publish_time for private packages (#1061)
|
||||
|
||||
2.17.0 / 2016-11-03
|
||||
==================
|
||||
|
||||
* feat: make snyk.io url configable (#1058)
|
||||
|
||||
2.16.2 / 2016-09-27
|
||||
==================
|
||||
|
||||
* fix: try to use config.registryHost first on setDownloadURL (#1044)
|
||||
|
||||
2.16.1 / 2016-08-22
|
||||
==================
|
||||
|
||||
* refactor: refine publishable's code (#1022)
|
||||
|
||||
2.16.0 / 2016-08-22
|
||||
==================
|
||||
|
||||
* feat: admin can do everything (#1021)
|
||||
|
||||
2.15.0 / 2016-08-22
|
||||
==================
|
||||
|
||||
* feat: return dist-tag on package registry ([#1020](https://github.com/cnpm/cnpmjs.org/issues/1020))
|
||||
* chore(package): update supertest to version 2.0.0 ([#1004](https://github.com/cnpm/cnpmjs.org/issues/1004))
|
||||
|
||||
2.14.0 / 2016-08-04
|
||||
==================
|
||||
|
||||
* feat: password may contains ":" ([#999](https://github.com/cnpm/cnpmjs.org/issues/999))
|
||||
* fix: limit sync fails email notice ([#1006](https://github.com/cnpm/cnpmjs.org/issues/1006))
|
||||
|
||||
2.13.0 / 2016-07-26
|
||||
==================
|
||||
|
||||
* feat: enable maxrequests middleware ([#1003](https://github.com/cnpm/cnpmjs.org/issues/1003))
|
||||
|
||||
2.12.2 / 2016-07-11
|
||||
==================
|
||||
|
||||
* fix: getModuleByRange don't list all packages ([#990](https://github.com/cnpm/cnpmjs.org/issues/990))
|
||||
* fix: should show new version package count ([#984](https://github.com/cnpm/cnpmjs.org/issues/984))
|
||||
|
||||
2.12.1 / 2016-07-01
|
||||
==================
|
||||
|
||||
* fix: make sure chagnes stream destroy ([#982](https://github.com/cnpm/cnpmjs.org/issues/982))
|
||||
* chore(package): update semver to version 5.2.0 ([#978](https://github.com/cnpm/cnpmjs.org/issues/978))
|
||||
* deps: use ^ instead of ~ ([#976](https://github.com/cnpm/cnpmjs.org/issues/976))
|
||||
* chore(package): update mini-logger to version 1.1.1 ([#973](https://github.com/cnpm/cnpmjs.org/issues/973))
|
||||
|
||||
2.12.0 / 2016-06-26
|
||||
==================
|
||||
|
||||
* fix: logger seperator should be one EOL ([#972](https://github.com/cnpm/cnpmjs.org/issues/972))
|
||||
* feat: add security check badge for public package ([#971](https://github.com/cnpm/cnpmjs.org/issues/971))
|
||||
|
||||
2.11.0 / 2016-06-25
|
||||
==================
|
||||
|
||||
* feat: add changes stream syncer ([#970](https://github.com/cnpm/cnpmjs.org/issues/970))
|
||||
* chore(package): update pg to version 5.1.0 ([#953](https://github.com/cnpm/cnpmjs.org/issues/953))
|
||||
|
||||
2.10.1 / 2016-06-05
|
||||
==================
|
||||
|
||||
* fix: should sync missing public scoped package on install ([#946](https://github.com/cnpm/cnpmjs.org/issues/946))
|
||||
* chore(package): update bytes to version 2.4.0 ([#943](https://github.com/cnpm/cnpmjs.org/issues/943))
|
||||
* userService ([#926](https://github.com/cnpm/cnpmjs.org/issues/926))
|
||||
* chore(package): update should to version 8.4.0 ([#928](https://github.com/cnpm/cnpmjs.org/issues/928))
|
||||
* chore(package): update humanize-ms to version 1.2.0 ([#927](https://github.com/cnpm/cnpmjs.org/issues/927))
|
||||
* chore(package): update kcors to version 1.2.1 ([#918](https://github.com/cnpm/cnpmjs.org/issues/918))
|
||||
* chore(package): update urllib to version 2.9.0 ([#898](https://github.com/cnpm/cnpmjs.org/issues/898))
|
||||
|
||||
2.10.0 / 2016-04-15
|
||||
==================
|
||||
|
||||
* feat: show tarball url on package page ([#894](https://github.com/cnpm/cnpmjs.org/issues/894))
|
||||
* chore(package): update koa-mock to version 1.6.1 ([#891](https://github.com/cnpm/cnpmjs.org/issues/891))
|
||||
|
||||
2.9.5 / 2016-04-12
|
||||
==================
|
||||
|
||||
* fix: change logo url to a better https source
|
||||
* fix: http://cnpmjs.org/package/fms pre style ([#739](https://github.com/cnpm/cnpmjs.org/issues/739))
|
||||
|
||||
2.9.4 / 2016-04-09
|
||||
==================
|
||||
|
||||
* fix: don't sync constructor package on exists mode ([#883](https://github.com/cnpm/cnpmjs.org/issues/883))
|
||||
* Update utility to version 1.7.0 🚀
|
||||
* chore: update sponsor link
|
||||
|
||||
2.9.3 / 2016-04-05
|
||||
==================
|
||||
|
||||
* fix: use better diff time to check sync status
|
||||
* Update sequelize to version 3.21.0 🚀
|
||||
* chore(package): update agentkeepalive to version 2.1.0
|
||||
* chore(package): update pg to version 4.5.2
|
||||
|
||||
2.9.2 / 2016-03-29
|
||||
==================
|
||||
|
||||
* fix: override antd for ul & ol list number & icon.
|
||||
|
||||
2.9.1 / 2016-03-29
|
||||
==================
|
||||
|
||||
* refactor: add more ua info on syncer
|
||||
|
||||
2.9.0 / 2016-03-26
|
||||
==================
|
||||
|
||||
* feat: only admin can unpublish
|
||||
* chore(package): update gravatar to version 1.5.0
|
||||
* chore(package): update sequelize to version 3.20.0
|
||||
* fix: fix save download count unqiue constraint error
|
||||
* chore(package): update moment to version 2.12.0
|
||||
|
||||
2.8.1 / 2016-03-07
|
||||
==================
|
||||
|
||||
* fix: only send warning email if no any sync data after 24h
|
||||
* chore(package): update kcors to version 1.1.0
|
||||
* chore(package): update koa to version 1.2.0
|
||||
* chore(package): update urllib to version 2.8.0
|
||||
|
||||
2.8.0 / 2016-02-23
|
||||
==================
|
||||
|
||||
* fix: convert `*` to latest tag
|
||||
* deps: upgrade deps and remove node 2.0.0 support
|
||||
* doc: update sponsors on readme
|
||||
* fix: update copyright year
|
||||
* doc: fix disturl typo
|
||||
* deps: sequelize@3.19.0
|
||||
|
||||
2.7.1 / 2016-02-01
|
||||
==================
|
||||
|
||||
* fix(semver): when have invalid version([#817](https://github.com/cnpm/cnpmjs.org/issues/817))
|
||||
|
||||
2.7.0 / 2016-02-01
|
||||
==================
|
||||
|
||||
* test: fix all test cases
|
||||
* test: fix unpublish
|
||||
* test: add complex range test case
|
||||
* feat: support semver([#816](https://github.com/cnpm/cnpmjs.org/issues/816))
|
||||
|
||||
2.6.2 / 2016-01-19
|
||||
==================
|
||||
|
||||
* feat: list & show support jsonp
|
||||
* chore(package): update urllib to version 2.7.0
|
||||
* Delete install.md
|
||||
|
||||
2.6.1 / 2016-01-12
|
||||
==================
|
||||
|
||||
* fix: source registry is not cnpm, ignore check status
|
||||
|
||||
2.6.0 / 2016-01-12
|
||||
==================
|
||||
|
||||
* feat(sync): monitor sync status
|
||||
* chore(package): update agentkeepalive to version 2.0.3
|
||||
* fix SequelizeDatabaseError: ER_NO_SUCH_TABLE: Table 'qnpm.total' doesn't exist\nreproduce this bug:\nthe first startup of cnpmjs.org
|
||||
* chore(package): update moment to version 2.11.0
|
||||
* chore(package): update xss to version 0.2.10
|
||||
* chore(package): update pg-hstore to version 2.3.2
|
||||
* chore(package): update mini-logger to version 1.1.0
|
||||
* chore(package): update urllib to version 2.6.0
|
||||
* fix: row.package will json parse error
|
||||
* remove bluebird
|
||||
* chore(package): update utility to version 1.6.0
|
||||
|
||||
2.5.1 / 2015-12-02
|
||||
==================
|
||||
|
||||
* chore(package): update bluebird to version 3.0.6
|
||||
* fix: SequelizeDatabaseError
|
||||
* fix(dist_tag): disable delete latest tag
|
||||
* feat: count total private pkgs
|
||||
* fix: use isoweek. a week start from monday
|
||||
* chore(package): update xss to version 0.2.8
|
||||
* chore(package): update semver to version 5.1.0
|
||||
|
||||
2.5.0 / 2015-11-17
|
||||
==================
|
||||
|
||||
* test: add node v5
|
||||
* feat(sync): sync deleted user
|
||||
* Update show.js
|
||||
* chore(package): update bytes to version 2.2.0
|
||||
* do not sync inner username
|
||||
* gzip static file
|
||||
* chore(package): update bytes to version 2.1.0
|
||||
* chore(package): update is-type-of to version 1.0.0
|
||||
* Update static.js
|
||||
* chore(package): update cfork to version 1.4.0
|
||||
* chore(package): update bluebird to version 3.0.5
|
||||
|
||||
2.4.1 / 2015-10-27
|
||||
==================
|
||||
|
||||
* fix: improve registry index page performance with cache
|
||||
* Configable badge URL prefix.
|
||||
* chore(package): update koa-mock to version 1.5.0
|
||||
* chore(package): update urllib to version 2.5.0
|
||||
* chore(package): update co to version 4.6.0
|
||||
* chore(package): update semver to version 5.0.3
|
||||
|
||||
2.4.0 / 2015-10-21
|
||||
==================
|
||||
|
||||
* feat(registry): add package's dependents api
|
||||
* fix: show package's dependents
|
||||
* chore(package): update utility to version 1.5.0
|
||||
* chore(package): update bluebird to version 2.10.2
|
||||
|
||||
2.3.1 / 2015-10-15
|
||||
==================
|
||||
|
||||
* refactor: remove gnode
|
||||
* deps: upgrade giturl
|
||||
* Some fixes
|
||||
|
||||
2.3.0 / 2015-10-15
|
||||
==================
|
||||
|
||||
* Add dev dependencies.
|
||||
* Package page fix.
|
||||
* refactor: add more sync log
|
||||
* Add mock data.
|
||||
* refactor: add more sync log
|
||||
* Fix sidebar overflow.
|
||||
* Merge pull request [#680](https://github.com/cnpm/cnpmjs.org/issues/680) from ibigbug/ant-design
|
||||
* Clean code.
|
||||
* Indent.
|
||||
* chore(package): update debug to version 2.2.0
|
||||
* chore(package): update should to version 7.1.0
|
||||
* chore(package): update koa to version 1.1.0
|
||||
* Remove default adBanner.
|
||||
* Package pages.
|
||||
* Common styles.
|
||||
* search page.
|
||||
* Sync page.
|
||||
* Profile page antd style.
|
||||
* Unpublished pkg page style.
|
||||
* Add page title for unpubed pkg.
|
||||
* Index page style use antd.
|
||||
* chore(package): update pg to version 4.4.2
|
||||
* chore(package): update cfork to version 1.3.1
|
||||
* chore(package): update moment to version 2.10.6
|
||||
* feat(badge): Use qiniu cdn
|
||||
|
||||
2.2.1 / 2015-09-30
|
||||
==================
|
||||
|
||||
@@ -76,10 +566,10 @@
|
||||
|
||||
* feat: support always-auth
|
||||
* fix mysql select args = [] bug
|
||||
* fix #597 sequelize raw query.
|
||||
* fix [#597](https://github.com/cnpm/cnpmjs.org/issues/597) sequelize raw query.
|
||||
* fix(markdown): hotfix markdown-it cpu problem
|
||||
* feat: upgrade to co4
|
||||
* use kcors fixes #594
|
||||
* use kcors fixes [#594](https://github.com/cnpm/cnpmjs.org/issues/594)
|
||||
|
||||
2.0.0-rc.13 / 2015-02-04
|
||||
==================
|
||||
@@ -690,7 +1180,7 @@
|
||||
* npm publish dont contains .jshint*
|
||||
* npm test run jshint
|
||||
* Add jshint check: $ make jshint
|
||||
* use `yield* next` instead of `yield next`
|
||||
* use `yield next` instead of `yield next`
|
||||
* replace dist.u.qiniudn.com with cnpmjs.org/dist
|
||||
|
||||
0.3.5 / 2014-03-05
|
||||
|
||||
@@ -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
|
||||
|
||||
71
Makefile
71
Makefile
@@ -1,45 +1,30 @@
|
||||
TESTS = $(shell ls -S `find test -type f -name "*.test.js" -print`)
|
||||
REPORTER = spec
|
||||
TIMEOUT = 30000
|
||||
TIMEOUT = 600000
|
||||
MOCHA_OPTS =
|
||||
DB = sqlite
|
||||
DISTURL = https://npm.taobao.org/mirrors/iojs
|
||||
BIN = iojs
|
||||
|
||||
ifeq ($(findstring io.js, $(shell which node)),)
|
||||
BIN = node
|
||||
DISTURL = https://npm.taobao.org/mirrors/node
|
||||
endif
|
||||
|
||||
install:
|
||||
@npm install --build-from-source --registry=http://registry.npm.taobao.org \
|
||||
--disturl=$(DISTURL)
|
||||
|
||||
install-production production:
|
||||
@NODE_ENV=production $(MAKE) install
|
||||
|
||||
jshint: install
|
||||
@-node_modules/.bin/jshint ./
|
||||
jshint:
|
||||
@node_modules/.bin/jshint .
|
||||
|
||||
init-database:
|
||||
@$(BIN) --harmony test/init_db.js
|
||||
@NODE_ENV=test node test/init_db.js
|
||||
|
||||
init-mysql:
|
||||
@mysql -uroot -e 'DROP DATABASE IF EXISTS cnpmjs_test;'
|
||||
@mysql -uroot -e 'CREATE DATABASE cnpmjs_test;'
|
||||
@mysql -uroot -h 127.0.0.1 --port 3306 -e 'DROP DATABASE IF EXISTS cnpmjs_test;'
|
||||
@mysql -uroot -h 127.0.0.1 --port 3306 -e 'CREATE DATABASE cnpmjs_test;'
|
||||
|
||||
init-pg:
|
||||
@psql -c 'DROP DATABASE IF EXISTS cnpmjs_test;'
|
||||
@psql -c 'CREATE DATABASE cnpmjs_test;'
|
||||
|
||||
test: install init-database
|
||||
test: init-database
|
||||
@NODE_ENV=test DB=${DB} node_modules/.bin/mocha \
|
||||
--harmony \
|
||||
--reporter $(REPORTER) \
|
||||
--timeout $(TIMEOUT) \
|
||||
--require intelli-espower-loader \
|
||||
--require should \
|
||||
--require should-http \
|
||||
--require co-mocha \
|
||||
--require thunk-mocha \
|
||||
--require ./test/init.js \
|
||||
$(MOCHA_OPTS) \
|
||||
$(TESTS)
|
||||
@@ -55,16 +40,16 @@ test-pg: init-pg
|
||||
|
||||
test-all: test-sqlite test-mysql
|
||||
|
||||
test-cov cov: install init-database
|
||||
@NODE_ENV=test DB=${DB} $(BIN) \
|
||||
node_modules/.bin/istanbul cover --preserve-comments \
|
||||
test-cov cov: init-database
|
||||
@NODE_ENV=test DB=${DB} node \
|
||||
node_modules/.bin/istanbul cover \
|
||||
node_modules/.bin/_mocha \
|
||||
-- -u exports \
|
||||
--reporter $(REPORTER) \
|
||||
--timeout $(TIMEOUT) \
|
||||
--require intelli-espower-loader \
|
||||
--require should \
|
||||
--require should-http \
|
||||
--require co-mocha \
|
||||
--require thunk-mocha \
|
||||
--require ./test/init.js \
|
||||
$(MOCHA_OPTS) \
|
||||
$(TESTS)
|
||||
@@ -75,17 +60,17 @@ test-cov-sqlite:
|
||||
test-cov-mysql: init-mysql
|
||||
@$(MAKE) test-cov DB=mysql
|
||||
|
||||
test-travis: install init-database
|
||||
@NODE_ENV=test DB=${DB} CNPM_SOURCE_NPM=http://registry.npmjs.com CNPM_SOURCE_NPM_ISCNPM=false \
|
||||
$(BIN) \
|
||||
node_modules/.bin/istanbul cover --preserve-comments \
|
||||
test-travis: init-database
|
||||
@NODE_ENV=test DB=${DB} \
|
||||
node \
|
||||
node_modules/.bin/istanbul cover \
|
||||
node_modules/.bin/_mocha \
|
||||
-- -u exports \
|
||||
--reporter dot \
|
||||
--timeout $(TIMEOUT) \
|
||||
--require intelli-espower-loader \
|
||||
--require should \
|
||||
--require should-http \
|
||||
--require co-mocha \
|
||||
--require thunk-mocha \
|
||||
--require ./test/init.js \
|
||||
$(MOCHA_OPTS) \
|
||||
$(TESTS)
|
||||
@@ -101,20 +86,20 @@ test-travis-pg:
|
||||
@psql -c 'CREATE DATABASE cnpmjs_test;' -U postgres
|
||||
@DB_PORT=5432 DB_USER=postgres $(MAKE) test-travis DB=postgres
|
||||
|
||||
test-travis-all: test-travis-sqlite test-travis-mysql test-travis-pg
|
||||
test-travis-all: jshint test-travis-sqlite test-travis-mysql test-travis-pg
|
||||
|
||||
dev:
|
||||
@NODE_ENV=development $(BIN) node_modules/.bin/node-dev --harmony dispatch.js
|
||||
@NODE_ENV=development node node_modules/.bin/node-dev dispatch.js
|
||||
|
||||
contributors: install
|
||||
contributors:
|
||||
@node_modules/.bin/contributors -f plain -o AUTHORS
|
||||
|
||||
autod: install
|
||||
autod:
|
||||
@node_modules/.bin/autod -w \
|
||||
--prefix "~" \
|
||||
--registry https://r.cnpmjs.org \
|
||||
--prefix "^" \
|
||||
--exclude public,view,docs,backup,coverage \
|
||||
--dep bluebird,mysql \
|
||||
--keep should,supertest,should-http,chunkstream,mm,pedding
|
||||
@$(MAKE) install
|
||||
--dep mysql \
|
||||
--keep should,supertest,chunkstream,mm,pedding
|
||||
|
||||
.PHONY: test
|
||||
|
||||
135
README.md
135
README.md
@@ -1,84 +1,77 @@
|
||||
cnpmjs.org
|
||||
=======
|
||||
|
||||
[![NPM version][npm-image]][npm-url]
|
||||
[![npm version][npm-image]][npm-url]
|
||||
[![build status][travis-image]][travis-url]
|
||||
[![Test coverage][cov-image]][cov-url]
|
||||
[![David deps][david-image]][david-url]
|
||||
[![Test coverage][codecov-image]][codecov-url]
|
||||
[![Known Vulnerabilities][snyk-image]][snyk-url]
|
||||
[![npm download][download-image]][download-url]
|
||||
|
||||
[npm-image]: http://cnpmjs.org/badge/v/cnpmjs.org.svg?style=flat-square
|
||||
[npm-url]: http://cnpmjs.org/package/cnpmjs.org
|
||||
[travis-image]: https://img.shields.io/travis/cnpm/cnpmjs.org.svg?style=flat-square
|
||||
[travis-url]: https://travis-ci.org/cnpm/cnpmjs.org
|
||||
[cov-image]: http://codecov.io/github/cnpm/cnpmjs.org/coverage.svg?branch=master
|
||||
[cov-url]: http://codecov.io/github/cnpm/cnpmjs.org?branch=master
|
||||
[david-image]: https://img.shields.io/david/cnpm/cnpmjs.org.svg?style=flat-square
|
||||
[david-url]: https://david-dm.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
|
||||
|
||||

|
||||
|
||||
## What is this?
|
||||
## Description
|
||||
|
||||
Private npm registry and web for Enterprise, base on [koa](http://koajs.com/),
|
||||
MySQL and [Simple Store Service](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide).
|
||||
|
||||
Our goal is to provide a low cost maintenance and easy to use solution for private npm.
|
||||
Our goal is to provide a low cost maintenance, easy to use, and easy to scale solution for private npm.
|
||||
|
||||
## What can you do with `cnpmjs.org`
|
||||
## What can you do with `cnpmjs.org`?
|
||||
|
||||
* Build a private npm for your own enterprise. ([alibaba](http://www.alibaba.com/) is using `cnpmjs.org` now)
|
||||
* Build a mirror NPM. (we use it to build a mirror in China: [cnpmjs.org](http://cnpmjs.org/))
|
||||
* Build a completely independent NPM registry to store whatever you like.
|
||||
* Build a npm mirror. (we use it to build a mirror in China: [https://npm.taobao.org/](https://npm.taobao.org/))
|
||||
* Use the private npm service provided by Alibaba Cloud DevOps which build with cnpm. [https://packages.aliyun.com/](https://packages.aliyun.com/?channel=pd_cnpm_github)
|
||||
|
||||
## Features
|
||||
|
||||
* **Support "scoped" packages**: [npm/npm#5239](https://github.com/npm/npm/issues/5239)
|
||||
* **Support [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing)**
|
||||
* **Simple to deploy**: only need `mysql` and a [simple store system](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide).
|
||||
You can get the source code through `npm` or `git`.
|
||||
* **Low cost and easy maintenance**: `package.json` info store in MySQL, MariaDB, SQLite or PostgreSQL databases,
|
||||
tarball(tgz file) store in CDN or other store systems.
|
||||
* **Automatic synchronization**: automatic synchronization from any registry specified, support two sync modes:
|
||||
- Sync all modules from a specified registry, like [npm registry](http://registry.npmjs.org).
|
||||
- Only sync the modules that exists in your own registry.
|
||||
* **Manual synchronization**: automatic synchronization may has little delay, but you can syn immediately by manually.
|
||||
* **Low cost and easy maintenance**: `package.json` info can store in MySQL, MariaDB, SQLite or PostgreSQL.
|
||||
tarball(tgz file) can store in Amazon S3 or other object storage service.
|
||||
* **Automatic synchronization**: automatically sync from any registry specified. support two sync modes:
|
||||
- Sync all modules from upstream
|
||||
- Only sync the modules after first access.
|
||||
* **Manual synchronization**: automatic synchronization may has little delay. you can sync manually on web page.
|
||||
* **Customized client**: we provide a client [cnpm](https://github.com/cnpm/cnpm)
|
||||
to extend `npm` with more features(`sync` command, [gzip](https://github.com/npm/npm-registry-client/pull/40) support).
|
||||
And it easy to wrap for your own registry which build with `cnpmjs.org`.
|
||||
* **Compatible with NPM client**: you can use the origin NPM client with `cnpmjs.org`,
|
||||
only need to change the registry in config. Even include manual synchronization (through `install` command).
|
||||
* **Version badge**: base on [shields.io](http://shields.io/) 
|
||||
* **Support http_proxy**: if you're behind firewall, need to request through http proxy
|
||||
And it is easy to wrap for your own registry which build with `cnpmjs.org`.
|
||||
* **Compatible with npm client**: you can use the official npm client with `cnpmjs.org`.
|
||||
you only need to change the registry in client config.
|
||||
* **Support http_proxy**: if you're behind a firewall, you can provide a http proxy for cnpmjs.org.
|
||||
|
||||
**PROTIP** Be sure to read [Migrating from 1.x to 2.x](https://github.com/cnpm/cnpmjs.org/wiki/Migrating-from-1.x-to-2.x)
|
||||
as well as [New features in 2.x](https://github.com/cnpm/cnpmjs.org/wiki/New-features-in-2.x).
|
||||
## Docs
|
||||
|
||||
## Getting Start
|
||||
|
||||
* [Deploy a private npm registry in 5 minutes](https://github.com/cnpm/cnpmjs.org/wiki/Deploy-a-private-npm-registry-in-5-minutes)
|
||||
* @[dead-horse](https://github.com/dead-horse): [What is cnpm?](http://deadhorse.me/slides/cnpmjs.html)
|
||||
* install and deploy cnpmjs.org through npm: [examples](https://github.com/cnpm/custom-cnpm-example)
|
||||
* Mirror NPM in China: [cnpmjs.org](http://cnpmjs.org)
|
||||
* [How to deploy](https://github.com/cnpm/cnpmjs.org/wiki/Deploy)
|
||||
* cnpm client: [cnpm](https://github.com/cnpm/cnpm), `npm install -g cnpm`
|
||||
* [How to deploy cnpmjs.org](https://github.com/cnpm/cnpmjs.org/wiki/Deploy)
|
||||
* [Sync packages through `http_proxy`](https://github.com/cnpm/cnpmjs.org/wiki/Sync-packages-through-http_proxy)
|
||||
* [Migrating from 1.x to 2.x](https://github.com/cnpm/cnpmjs.org/wiki/Migrating-from-1.x-to-2.x)
|
||||
* [New features in 2.x](https://github.com/cnpm/cnpmjs.org/wiki/New-features-in-2.x).
|
||||
* [wiki](https://github.com/cnpm/cnpmjs.org/wiki)
|
||||
|
||||
## Develop on your local machine
|
||||
|
||||
### Dependencies
|
||||
|
||||
* [node](http://nodejs.org) >=0.11.12, use `--harmony`
|
||||
* [node](http://nodejs.org) >= 8.0.0
|
||||
* Databases: only required one type
|
||||
* [sqlite3](https://npm.taobao.org/package/sqlite3) >= 3.0.2, we use `sqlite3` by default
|
||||
* [MySQL](http://dev.mysql.com/downloads/) >= 0.5.0, include `mysqld` and `mysql cli`. I test on `mysql@5.6.16`.
|
||||
* [MySQL](http://dev.mysql.com/downloads/) >= 5.6.16, include `mysqld` and `mysql cli`. I test on `mysql@5.6.16`.
|
||||
* MariaDB
|
||||
* PostgreSQL
|
||||
|
||||
### Clone codes and run test
|
||||
### Clone code and run test
|
||||
|
||||
```bash
|
||||
# clone from git
|
||||
@@ -100,6 +93,61 @@ $ make autod
|
||||
$ make dev
|
||||
```
|
||||
|
||||
### Dockerized cnpmjs.org Installation Guide
|
||||
|
||||
Cnpmjs.org shipped with a simple but pragmatic Docker Compose configuration.With the configuration, you can set up a MySQL backend cnpmjs.org instance by executing just one command on Docker installed environment.
|
||||
|
||||
#### Preparation
|
||||
|
||||
* [Install Docker](https://www.docker.com/community-edition)
|
||||
* [Install Docker Compose](https://docs.docker.com/compose/install/) (Docker for Mac, Docker for Windows include Docker Compose, so most Mac and Windows users do not need to install Docker Compose separately)
|
||||
* (Optional) Speed up Docker images downloading by setting up [Docker images download accelerator](https://yq.aliyun.com/articles/29941)
|
||||
|
||||
|
||||
#### Dockerized cnpmjs.org control command
|
||||
|
||||
Make sure your current working directory is the root of this GitHub repository.
|
||||
|
||||
##### Run dockerized cnpmjs.org
|
||||
|
||||
```bash
|
||||
$docker-compose up
|
||||
```
|
||||
|
||||
This command will build a Docker image using the current code of repository. Then set up a dockerized MySQL instance with data initialized. After Docker container running, you can access your cnpmjs.org web portal at http://127.0.0.1:7002 and npm register at http://127.0.0.1:7001.
|
||||
|
||||
#### Run cnpmjs.org in the backend
|
||||
|
||||
```bash
|
||||
$docker-compose up -d
|
||||
```
|
||||
|
||||
#### Rebuild cnpmjs.org Docker image
|
||||
|
||||
```bash
|
||||
$docker-compose build
|
||||
```
|
||||
|
||||
#### Remove current dockerized cnpmjs.org instance
|
||||
|
||||
The current configuration set 2 named Docker Volume for your persistent data. If you haven't change the repository directory name, them will be "cnpmjsorg_cnpm-files-volume" & "cnpmjsorg_cnpm-db-volume".
|
||||
|
||||
Be Careful, the following commands will remove them.
|
||||
|
||||
```bash
|
||||
$docker-compose rm
|
||||
$docker volume rm cnpmjsorg_cnpm-files-volume
|
||||
$docker volume rm cnpmjsorg_cnpm-db-volume
|
||||
```
|
||||
|
||||
You can get more information about your data volumes using the below commands:
|
||||
|
||||
```bash
|
||||
$docker volume ls // list all of your Docker volume
|
||||
$docker volume inspect cnpmjsorg_cnpm-files-volume
|
||||
$docker volume inspect cnpmjsorg_cnpm-db-volume
|
||||
```
|
||||
|
||||
## How to contribute
|
||||
|
||||
* Clone the project
|
||||
@@ -109,9 +157,22 @@ $ make dev
|
||||
|
||||
Tips: make sure your code is following the [node-style-guide](https://github.com/felixge/node-style-guide).
|
||||
|
||||
## Top contributors
|
||||
|
||||
|
||||
[](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/0)
|
||||
[](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/1)
|
||||
[](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/2)
|
||||
[](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/3)
|
||||
[](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/4)
|
||||
[](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/5)
|
||||
[](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/6)
|
||||
[](https://sourcerer.io/fame/fengmk2/cnpm/cnpmjs.org/links/7)
|
||||
|
||||
|
||||
## Sponsors
|
||||
|
||||
- [](http://www.ucloud.cn?sem=sdk-CNPMJS)
|
||||
- [](http://click.aliyun.com/m/4288/) [](https://devops.aliyun.com/?channel=pd_cnpm_github) (2016.2 - now)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
24
bin/change_password.js
Normal file
24
bin/change_password.js
Normal 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);
|
||||
});
|
||||
93
bin/cli.js
93
bin/cli.js
@@ -1,9 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**!
|
||||
* cnpmjs.org - bin/cli.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* Copyright(c) cnpm and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
@@ -16,7 +14,6 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
require('gnode');
|
||||
var debug = require('debug')('cnpmjs.org:cli');
|
||||
var program = require('commander');
|
||||
var path = require('path');
|
||||
@@ -51,51 +48,59 @@ program.parse(process.argv);
|
||||
|
||||
|
||||
function start(options) {
|
||||
stop(options);
|
||||
var dataDir = options.dataDir || path.join(process.env.HOME, '.cnpmjs.org');
|
||||
mkdirp.sync(dataDir);
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
// 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 + '');
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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');
|
||||
if (fs.existsSync(pidfile)) {
|
||||
return new Promise(function (resolve) {
|
||||
if (!fs.existsSync(pidfile)) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
var pid = Number(fs.readFileSync(pidfile, 'utf8'));
|
||||
treekill(pid, function (err) {
|
||||
if (err) {
|
||||
@@ -104,11 +109,12 @@ function stop(options) {
|
||||
}
|
||||
console.log('cnpmjs.org server:%d stop', pid);
|
||||
fs.unlinkSync(pidfile);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initDatabase() {
|
||||
function initDatabase(callback) {
|
||||
var models = require('../models');
|
||||
|
||||
models.sequelize.sync({ force: false })
|
||||
@@ -120,6 +126,7 @@ function initDatabase() {
|
||||
throw err;
|
||||
} else {
|
||||
console.log('[models/init_script.js] `sqlite` sequelize sync and init success');
|
||||
callback();
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -7,7 +7,7 @@ export NODE_ENV='production'
|
||||
ulimit -c unlimited
|
||||
|
||||
cd `dirname $0`/..
|
||||
NODEJS='node --harmony'
|
||||
NODEJS='node'
|
||||
BASE_HOME=`pwd`
|
||||
PROJECT_NAME=`basename ${BASE_HOME}`
|
||||
STDOUT_LOG=`$NODEJS -e "console.log(require('path').join(require('$BASE_HOME/config').logdir, 'nodejs_stdout.log'));\
|
||||
|
||||
16
common/cache.js
Normal file
16
common/cache.js
Normal 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;
|
||||
@@ -1,62 +1,56 @@
|
||||
/**!
|
||||
* cnpmjs.org - common/logger.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
const debug = require('debug')('cnpmjs.org:logger');
|
||||
const formater = require('error-formater');
|
||||
const Logger = require('mini-logger');
|
||||
const utility = require('utility');
|
||||
const util = require('util');
|
||||
const os = require('os');
|
||||
const config = require('../config');
|
||||
const mail = require('./mail');
|
||||
|
||||
var formater = require('error-formater');
|
||||
var Logger = require('mini-logger');
|
||||
var utility = require('utility');
|
||||
var util = require('util');
|
||||
var config = require('../config');
|
||||
var mail = require('./mail');
|
||||
const isTEST = process.env.NODE_ENV === 'test';
|
||||
const categories = ['sync_info', 'sync_error'];
|
||||
|
||||
var isTEST = process.env.NODE_ENV === 'test';
|
||||
var categories = ['sync_info', 'sync_error'];
|
||||
|
||||
var logger = module.exports = Logger({
|
||||
const logger = module.exports = Logger({
|
||||
categories: categories,
|
||||
dir: config.logdir,
|
||||
duration: '1d',
|
||||
format: '[{category}.]YYYY-MM-DD[.log]',
|
||||
stdout: config.debug && !isTEST,
|
||||
errorFormater: errorFormater
|
||||
errorFormater: errorFormater,
|
||||
seperator: os.EOL,
|
||||
});
|
||||
|
||||
var to = [];
|
||||
const to = [];
|
||||
for (var user in config.admins) {
|
||||
to.push(config.admins[user]);
|
||||
}
|
||||
|
||||
function errorFormater(err) {
|
||||
var msg = formater.both(err);
|
||||
const msg = formater.both(err);
|
||||
mail.error(to, msg.json.name, msg.text);
|
||||
return msg.text;
|
||||
}
|
||||
|
||||
logger.syncInfo = function () {
|
||||
var args = [].slice.call(arguments);
|
||||
const args = [].slice.call(arguments);
|
||||
if (typeof args[0] === 'string') {
|
||||
args[0] = util.format('[%s][%s] ', utility.logDate(), process.pid) + args[0];
|
||||
}
|
||||
if (debug.enabled) {
|
||||
debug.apply(debug, args);
|
||||
}
|
||||
logger.sync_info.apply(logger, args);
|
||||
};
|
||||
|
||||
logger.syncError =function () {
|
||||
var args = [].slice.call(arguments);
|
||||
const args = [].slice.call(arguments);
|
||||
if (typeof args[0] === 'string') {
|
||||
args[0] = util.format('[%s][%s] ', utility.logDate(), process.pid) + args[0];
|
||||
}
|
||||
if (debug.enabled) {
|
||||
debug.apply(debug, args);
|
||||
}
|
||||
logger.sync_error.apply(logger, arguments);
|
||||
};
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
/**!
|
||||
* 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 nodemailer = require('nodemailer');
|
||||
var utility = require('utility');
|
||||
var os = require('os');
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - common/markdown.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var xss = require('xss');
|
||||
var MarkdownIt = require('markdown-it');
|
||||
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - common/sequelize.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Sequelize = require('sequelize');
|
||||
var DataTypes = require('sequelize/lib/data-types');
|
||||
var config = require('../config');
|
||||
@@ -35,6 +21,7 @@ if (config.mysqlServers && config.database.dialect === 'sqlite') {
|
||||
|
||||
console.warn('[WARNNING] your config.js was too old, please @see https://github.com/cnpm/cnpmjs.org/wiki/Migrating-from-1.x-to-2.x');
|
||||
var server = config.mysqlServers[0];
|
||||
var dialectOptions = config.database && config.database.dialectOptions;
|
||||
config.database = {
|
||||
db: config.mysqlDatabase,
|
||||
username: server.user,
|
||||
@@ -45,10 +32,13 @@ if (config.mysqlServers && config.database.dialect === 'sqlite') {
|
||||
pool: {
|
||||
maxConnections: config.mysqlMaxConnections || 10,
|
||||
minConnections: 0,
|
||||
maxIdleTime: 30000
|
||||
maxIdleTime: 30000,
|
||||
},
|
||||
logging: !!process.env.SQL_DEBUG,
|
||||
};
|
||||
if (dialectOptions) {
|
||||
config.database.dialectOptions = dialectOptions;
|
||||
}
|
||||
}
|
||||
|
||||
var database = config.database;
|
||||
|
||||
@@ -1,24 +1,12 @@
|
||||
/**!
|
||||
* cnpmjs.org - common/urllib.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var urlparse = require('url').parse;
|
||||
var urllib = require('urllib');
|
||||
var HttpAgent = require('agentkeepalive');
|
||||
var HttpsAgent = require('agentkeepalive').HttpsAgent;
|
||||
var config = require('../config');
|
||||
var url = require('url');
|
||||
var URL = require('url').URL;
|
||||
|
||||
var httpAgent;
|
||||
var httpsAgent;
|
||||
@@ -71,5 +59,26 @@ var client = urllib.create({
|
||||
httpsAgent: httpsAgent
|
||||
});
|
||||
|
||||
var request = urllib.HttpClient.prototype.request;
|
||||
|
||||
function getAccelerateUrl(url) {
|
||||
const urlObj = typeof url === 'string' ? new URL(url) : url;
|
||||
const newHost = config.accelerateHostMap && config.accelerateHostMap[urlObj.host];
|
||||
if (newHost) {
|
||||
urlObj.host = newHost;
|
||||
}
|
||||
return urlObj.toString();
|
||||
}
|
||||
|
||||
client.request = function (requestUrl, options) {
|
||||
const accelerateUrl = getAccelerateUrl(requestUrl);
|
||||
options = Object.assign({}, options, {
|
||||
formatRedirectUrl: function (from, to) {
|
||||
return getAccelerateUrl(url.resolve(from, to));
|
||||
}
|
||||
});
|
||||
return Reflect.apply(request, client, [ accelerateUrl, options ]);
|
||||
};
|
||||
|
||||
module.exports = client;
|
||||
module.exports.USER_AGENT = urllib.USER_AGENT;
|
||||
|
||||
153
config/index.js
153
config/index.js
@@ -1,31 +1,20 @@
|
||||
/**!
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com>
|
||||
* fengmk2 <m@fengmk2.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var mkdirp = require('mkdirp');
|
||||
var copy = require('copy-to');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var os = require('os');
|
||||
var utility = require('utility');
|
||||
|
||||
var version = require('../package.json').version;
|
||||
|
||||
var Nfs = require('fs-cnpm');
|
||||
var root = path.dirname(__dirname);
|
||||
var dataDir = path.join(process.env.HOME || root, '.cnpmjs.org');
|
||||
|
||||
var config = {
|
||||
version: version,
|
||||
dataDir: dataDir,
|
||||
|
||||
/**
|
||||
* Cluster mode
|
||||
@@ -40,6 +29,11 @@ var config = {
|
||||
registryPort: 7001,
|
||||
webPort: 7002,
|
||||
bindingHost: '127.0.0.1', // only binding on 127.0.0.1 for local access
|
||||
// default is ctx.protocol
|
||||
protocol: '',
|
||||
// When sync package, cnpm not know the access protocol.
|
||||
// So should set manually
|
||||
backupProtocol: 'http',
|
||||
|
||||
// debug mode
|
||||
// if in debug mode, some middleware like limit wont load
|
||||
@@ -58,6 +52,21 @@ var config = {
|
||||
// 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: {
|
||||
@@ -93,8 +102,9 @@ var config = {
|
||||
}
|
||||
},
|
||||
|
||||
logoURL: '//ww4.sinaimg.cn/large/69c1d4acgw1ebfly5kjlij208202oglr.jpg', // cnpm logo image url
|
||||
logoURL: 'https://os.alipayobjects.com/rmsportal/oygxuIUkkrRccUz.jpg', // cnpm logo image url
|
||||
adBanner: '',
|
||||
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`
|
||||
@@ -132,6 +142,11 @@ var config = {
|
||||
maxIdleTime: 30000
|
||||
},
|
||||
|
||||
dialectOptions: {
|
||||
// if your server run on full cpu load, please set trace to false
|
||||
trace: true,
|
||||
},
|
||||
|
||||
// the storage engine for 'sqlite'
|
||||
// default store into ~/.cnpmjs.org/data.sqlite
|
||||
storage: path.join(dataDir, 'data.sqlite'),
|
||||
@@ -139,12 +154,23 @@ var config = {
|
||||
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')({
|
||||
nfs: new Nfs({
|
||||
dir: path.join(dataDir, 'nfs')
|
||||
}),
|
||||
// if set true, will 302 redirect to `nfs.url(dist.key)`
|
||||
downloadRedirectToNFS: false,
|
||||
// don't check database and just download tgz from nfs
|
||||
downloadTgzDontCheckModule: false,
|
||||
// remove original tarball when publishing
|
||||
unpublishRemoveTarball: true,
|
||||
|
||||
// registry url name
|
||||
registryHost: 'r.cnpmjs.org',
|
||||
@@ -174,12 +200,15 @@ var config = {
|
||||
// cnpm wont directly sync from this one
|
||||
// but sometimes will request it for some package infomations
|
||||
// please don't change it if not necessary
|
||||
officialNpmRegistry: 'https://registry.npmjs.org',
|
||||
officialNpmRegistry: 'https://registry.npmjs.com',
|
||||
officialNpmReplicate: 'https://replicate.npmjs.com',
|
||||
cnpmRegistry: 'https://r.cnpmjs.com',
|
||||
|
||||
// sync source, upstream registry
|
||||
// If you want to directly sync from official npm's registry
|
||||
// please drop them an email first
|
||||
sourceNpmRegistry: 'http://registry.npm.taobao.org',
|
||||
sourceNpmRegistry: 'https://registry.npm.taobao.org',
|
||||
sourceNpmWeb: 'https://npm.taobao.org',
|
||||
|
||||
// upstream registry is base on cnpm/cnpmjs.org or not
|
||||
// if your upstream is official npm registry, please turn it off
|
||||
@@ -193,6 +222,8 @@ var config = {
|
||||
// exist: only sync exist modules
|
||||
// all: sync all modules
|
||||
syncModel: 'none', // 'none', 'all', 'exist'
|
||||
// sync package.json/dist-tag.json to sync dir
|
||||
syncBackupFiles: false,
|
||||
|
||||
syncConcurrency: 1,
|
||||
// sync interval, default is 10 minutes
|
||||
@@ -208,11 +239,40 @@ var config = {
|
||||
|
||||
// sync devDependencies or not, default is false
|
||||
syncDevDependencies: false,
|
||||
// try to remove all deleted versions from original registry
|
||||
syncDeletedVersions: true,
|
||||
|
||||
// badge subject on http://shields.io/
|
||||
// changes streaming sync
|
||||
syncChangesStream: false,
|
||||
syncDownloadOptions: {
|
||||
// formatRedirectUrl: function (url, location)
|
||||
},
|
||||
handleSyncRegistry: 'http://127.0.0.1:7001',
|
||||
|
||||
// default badge subject
|
||||
badgeSubject: 'cnpm',
|
||||
// defautl use https://badgen.net/
|
||||
badgeService: {
|
||||
url: function(subject, status, options) {
|
||||
options = options || {};
|
||||
let url = `https://badgen.net/badge/${utility.encodeURIComponent(subject)}/${utility.encodeURIComponent(status)}`;
|
||||
if (options.color) {
|
||||
url += `/${utility.encodeURIComponent(options.color)}`;
|
||||
}
|
||||
if (options.icon) {
|
||||
url += `?icon=${utility.encodeURIComponent(options.icon)}`;
|
||||
}
|
||||
return url;
|
||||
},
|
||||
},
|
||||
|
||||
packagephobiaURL: 'https://packagephobia.now.sh',
|
||||
packagephobiaSupportPrivatePackage: false,
|
||||
packagephobiaMinDownloadCount: 1000,
|
||||
|
||||
// custom user service, @see https://github.com/cnpm/cnpmjs.org/wiki/Use-Your-Own-User-Authorization
|
||||
// when you not intend to ingegrate with your company's user system, then use null, it would
|
||||
// use the default cnpm user system
|
||||
userService: null,
|
||||
|
||||
// always-auth https://docs.npmjs.com/misc/config#always-auth
|
||||
@@ -222,8 +282,63 @@ var config = {
|
||||
// if you're behind firewall, need to request through http proxy, please set this
|
||||
// e.g.: `httpProxy: 'http://proxy.mycompany.com:8080'`
|
||||
httpProxy: null,
|
||||
|
||||
// snyk.io root url
|
||||
snykUrl: 'https://snyk.io',
|
||||
|
||||
// https://github.com/cnpm/cnpmjs.org/issues/1149
|
||||
// if enable this option, must create module_abbreviated and package_readme table in database
|
||||
enableAbbreviatedMetadata: false,
|
||||
|
||||
// global hook function: function* (envelope) {}
|
||||
// envelope format please see https://github.com/npm/registry/blob/master/docs/hooks/hooks-payload.md#payload
|
||||
globalHook: null,
|
||||
|
||||
opensearch: {
|
||||
host: '',
|
||||
},
|
||||
|
||||
// redis cache
|
||||
redisCache: {
|
||||
enable: false,
|
||||
connectOptions: null,
|
||||
},
|
||||
|
||||
// custom format full package list
|
||||
// change `GET /:name` request response body
|
||||
// use on `controllers/registry/list.js`
|
||||
formatCustomFullPackageInfoAndVersions: (ctx, packageInfo) => {
|
||||
return packageInfo;
|
||||
},
|
||||
// custom format one package version
|
||||
// change `GET /:name/:version` request response body
|
||||
// use on `controllers/registry/show.js`
|
||||
formatCustomOnePackageVersion: (ctx, packageVersion) => {
|
||||
return packageVersion;
|
||||
},
|
||||
// registry download accelerate map
|
||||
accelerateHostMap: {},
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
config.enableAbbreviatedMetadata = true;
|
||||
config.customRegistryMiddlewares.push((app) => {
|
||||
return function* (next) {
|
||||
this.set('x-custom-middleware', 'true');
|
||||
this.set('x-custom-app-models', typeof app.models.query === 'function' ? 'true' : 'false');
|
||||
yield next;
|
||||
};
|
||||
});
|
||||
|
||||
config.customWebMiddlewares.push((app) => {
|
||||
return function* (next) {
|
||||
this.set('x-custom-web-middleware', 'true');
|
||||
this.set('x-custom-web-app-models', typeof app.models.query === 'function' ? 'true' : 'false');
|
||||
yield next;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
var customConfig;
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/package/deprecate.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var packageService = require('../../../services/package');
|
||||
|
||||
module.exports = deprecateVersions;
|
||||
@@ -37,9 +23,10 @@ function* deprecateVersions() {
|
||||
if (!row) {
|
||||
// some version not exists
|
||||
this.status = 400;
|
||||
const error = '[version_error] Some versions: ' + JSON.stringify(Object.keys(body.versions)) + ' not found';
|
||||
this.body = {
|
||||
error: 'version_error',
|
||||
reason: 'Some versions: ' + JSON.stringify(Object.keys(body.versions)) + ' not found'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
@@ -51,10 +38,10 @@ function* deprecateVersions() {
|
||||
}
|
||||
yield updateTasks;
|
||||
// update last modified
|
||||
yield* packageService.updateModuleLastModified(name);
|
||||
yield packageService.updateModuleLastModified(name);
|
||||
|
||||
this.status = 201;
|
||||
this.body = {
|
||||
ok: true
|
||||
ok: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/package/dist_tag.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var packageService = require('../../../services/package');
|
||||
var hook = require('../../../services/hook');
|
||||
|
||||
function ok() {
|
||||
return {
|
||||
@@ -25,7 +12,7 @@ function ok() {
|
||||
// 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 rows = yield packageService.listModuleTags(name);
|
||||
var tags = {};
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var row = rows[i];
|
||||
@@ -38,7 +25,7 @@ exports.index = function* () {
|
||||
exports.save = function* () {
|
||||
var name = this.params.name || this.params[0];
|
||||
yield packageService.removeModuleTags(name);
|
||||
yield* exports.update.call(this);
|
||||
yield exports.update.call(this);
|
||||
};
|
||||
|
||||
// POST /-/package/:pkg/dist-tags -- Add/modify dist-tags from provided object body (merge)
|
||||
@@ -48,6 +35,19 @@ exports.update = function* () {
|
||||
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();
|
||||
@@ -63,9 +63,10 @@ exports.set = function* () {
|
||||
var pkg = yield packageService.getModule(name, version);
|
||||
if (!pkg) {
|
||||
this.status = 400;
|
||||
const error = '[version_error] ' + name + '@' + version + ' not exists';
|
||||
this.body = {
|
||||
error: 'version_error',
|
||||
reason: name + '@' + version + ' not exists'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
@@ -73,12 +74,44 @@ exports.set = function* () {
|
||||
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);
|
||||
};
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
/**!
|
||||
* Copyright(c) cnpm and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:registry:download');
|
||||
var mime = require('mime');
|
||||
var utility = require('utility');
|
||||
@@ -25,37 +13,59 @@ var packageService = require('../../../services/package');
|
||||
var downloadTotalService = require('../../../services/download_total');
|
||||
var config = require('../../../config');
|
||||
|
||||
var _downloads = {};
|
||||
let globalDownloads = new Map();
|
||||
|
||||
module.exports = function* download(next) {
|
||||
var name = this.params.name || this.params[0];
|
||||
var filename = this.params.filename || this.params[1];
|
||||
// scope pkg and download with out scope
|
||||
if (name.startsWith('@') && !filename.startsWith('@')) {
|
||||
var scope = name.slice(0, name.indexOf('/'));
|
||||
// fix filename with scope
|
||||
filename = `${scope}/${filename}`;
|
||||
}
|
||||
|
||||
|
||||
var version = filename.slice(name.length + 1, -4);
|
||||
var row = yield packageService.getModule(name, version);
|
||||
// can not get dist
|
||||
var url = null;
|
||||
|
||||
var query = this.query || {};
|
||||
// allow download from specific store bucket
|
||||
var options = query.bucket ? { bucket: query.bucket } : null;
|
||||
|
||||
if (typeof nfs.url === 'function') {
|
||||
if (is.generatorFunction(nfs.url)) {
|
||||
url = yield nfs.url(common.getCDNKey(name, filename));
|
||||
url = yield nfs.url(common.getCDNKey(name, filename), options);
|
||||
} else {
|
||||
url = nfs.url(common.getCDNKey(name, filename));
|
||||
url = nfs.url(common.getCDNKey(name, filename), options);
|
||||
}
|
||||
}
|
||||
|
||||
debug('download %s %s %s %s', name, filename, version, url);
|
||||
|
||||
if (!row || !row.package || !row.package.dist) {
|
||||
if (!url) {
|
||||
return yield* next;
|
||||
}
|
||||
// don't check database and just download tgz from nfs
|
||||
if (config.downloadTgzDontCheckModule && url) {
|
||||
this.status = 302;
|
||||
this.set('Location', url);
|
||||
_downloads[name] = (_downloads[name] || 0) + 1;
|
||||
const count = (globalDownloads.get(name) || 0) + 1;
|
||||
globalDownloads.set(name, count);
|
||||
return;
|
||||
}
|
||||
|
||||
_downloads[name] = (_downloads[name] || 0) + 1;
|
||||
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;
|
||||
@@ -84,19 +94,29 @@ module.exports = function* download(next) {
|
||||
this.body = yield downloadAsReadStream(dist.key);
|
||||
};
|
||||
|
||||
var saving = false;
|
||||
defer.setInterval(function* () {
|
||||
// save download count
|
||||
var totals = [];
|
||||
for (var name in _downloads) {
|
||||
var count = _downloads[name];
|
||||
totals.push([name, count]);
|
||||
}
|
||||
_downloads = {};
|
||||
|
||||
if (totals.length === 0) {
|
||||
if (saving) {
|
||||
return;
|
||||
}
|
||||
|
||||
// save download count
|
||||
var totals = [];
|
||||
var allCount = 0;
|
||||
for (const [ name, count ] of globalDownloads) {
|
||||
if (name !== '__all__') {
|
||||
totals.push([name, count]);
|
||||
}
|
||||
allCount += count;
|
||||
}
|
||||
globalDownloads = new Map();
|
||||
|
||||
if (allCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
saving = true;
|
||||
totals.push([ '__all__', allCount ]);
|
||||
debug('save download total: %j', totals);
|
||||
|
||||
var date = utility.YYYYMMDD();
|
||||
@@ -105,12 +125,16 @@ defer.setInterval(function* () {
|
||||
var name = item[0];
|
||||
var count = item[1];
|
||||
try {
|
||||
yield* downloadTotalService.plusModuleTotal({ name: name, date: date, count: count });
|
||||
yield downloadTotalService.plusModuleTotal({ name: name, date: date, count: count });
|
||||
} catch (err) {
|
||||
err.message += '; name: ' + name + ', count: ' + count + ', date: ' + date;
|
||||
logger.error(err);
|
||||
// save back to _downloads, try again next time
|
||||
_downloads[name] = (_downloads[name] || 0) + count;
|
||||
if (err.name !== 'SequelizeUniqueConstraintError') {
|
||||
err.message += '; name: ' + name + ', count: ' + count + ', date: ' + date;
|
||||
logger.error(err);
|
||||
}
|
||||
// save back to globalDownloads, try again next time
|
||||
count = (globalDownloads.get(name) || 0) + count;
|
||||
globalDownloads.set(name, count);
|
||||
}
|
||||
}
|
||||
}, 5000);
|
||||
saving = false;
|
||||
}, 5000 + Math.ceil(Math.random() * 1000));
|
||||
|
||||
@@ -1,34 +1,21 @@
|
||||
/*!
|
||||
* cnpmjs.org - controllers/registry/package/download_total.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (https://github.com/dead-horse)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var DownloadTotal = require('../../../services/download_total');
|
||||
var DATE_REG = /^\d{4}-\d{2}-\d{2}$/;
|
||||
|
||||
module.exports = function* downloadTotal () {
|
||||
var range = this.params.range;
|
||||
var name = this.params.name;
|
||||
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: 'range_error',
|
||||
reason: 'range must be YYYY-MM-DD:YYYY-MM-DD style'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,40 +1,73 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/package/list.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:registry:package:list');
|
||||
var utility = require('utility');
|
||||
var packageService = require('../../../services/package');
|
||||
var common = require('../../../lib/common');
|
||||
var SyncModuleWorker = require('../../sync_module_worker');
|
||||
var config = require('../../../config');
|
||||
const cache = require('../../../common/cache');
|
||||
const logger = require('../../../common/logger');
|
||||
|
||||
// https://forum.nginx.org/read.php?2,240120,240120#msg-240120
|
||||
// should set weak etag avoid nginx remove it
|
||||
function etag(objs) {
|
||||
return 'W/"' + utility.md5(JSON.stringify(objs)) + '"';
|
||||
}
|
||||
|
||||
/**
|
||||
* list all version of a module
|
||||
* GET /:name
|
||||
*/
|
||||
module.exports = function* list() {
|
||||
var orginalName = this.params.name || this.params[0];
|
||||
var name = orginalName;
|
||||
const name = this.params.name || this.params[0];
|
||||
// sync request will contain this query params
|
||||
let noCache = this.query.cache === '0';
|
||||
if (!noCache) {
|
||||
const ua = this.headers['user-agent'] || '';
|
||||
// old sync client will request with these user-agent
|
||||
if (ua.indexOf('npm_service.cnpmjs.org/') !== -1) {
|
||||
noCache = true;
|
||||
}
|
||||
}
|
||||
const isJSONPRequest = this.query.callback;
|
||||
let cacheKey;
|
||||
let needAbbreviatedMeta = false;
|
||||
let abbreviatedMetaType = 'application/vnd.npm.install-v1+json';
|
||||
if (config.enableAbbreviatedMetadata && this.accepts([ 'json', abbreviatedMetaType ]) === abbreviatedMetaType) {
|
||||
needAbbreviatedMeta = true;
|
||||
if (cache && !isJSONPRequest) {
|
||||
cacheKey = `list-${name}-v1`;
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheKey && !noCache) {
|
||||
const values = yield cache.hmget(cacheKey, 'etag', 'body');
|
||||
if (values && values[0] && values[1]) {
|
||||
this.body = values[1];
|
||||
this.type = 'json';
|
||||
this.etag = values[0];
|
||||
this.set('x-hit-cache', cacheKey);
|
||||
debug('hmget %s success, etag:%j', cacheKey, values[0]);
|
||||
if (config.registryCacheControlHeader) {
|
||||
this.set('cache-control', config.registryCacheControlHeader);
|
||||
}
|
||||
if (config.registryVaryHeader) {
|
||||
this.set('vary', config.registryVaryHeader);
|
||||
}
|
||||
return;
|
||||
}
|
||||
debug('hmget %s missing, %j', cacheKey, values);
|
||||
}
|
||||
|
||||
var rs = yield [
|
||||
packageService.getModuleLastModified(name),
|
||||
packageService.listModuleTags(name)
|
||||
packageService.listModuleTags(name),
|
||||
];
|
||||
var modifiedTime = rs[0];
|
||||
var tags = rs[1];
|
||||
|
||||
debug('show %s(%s), last modified: %s, tags: %j', name, orginalName, modifiedTime, tags);
|
||||
debug('show %s, last modified: %s, tags: %j', name, modifiedTime, tags);
|
||||
if (modifiedTime) {
|
||||
// find out the latest modfied time
|
||||
// because update tags only modfied tag, wont change module gmt_modified
|
||||
@@ -54,6 +87,20 @@ module.exports = function* list() {
|
||||
}
|
||||
}
|
||||
|
||||
if (needAbbreviatedMeta) {
|
||||
var rows = yield packageService.listModuleAbbreviatedsByName(name);
|
||||
if (rows.length > 0) {
|
||||
yield handleAbbreviatedMetaRequest(this, name, modifiedTime, tags, rows, cacheKey);
|
||||
return;
|
||||
}
|
||||
var fullRows = yield packageService.listModulesByName(name);
|
||||
if (fullRows.length > 0) {
|
||||
// no abbreviated meta rows, use the full meta convert to abbreviated meta
|
||||
yield handleAbbreviatedMetaRequestWithFullMeta(this, name, modifiedTime, tags, fullRows);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var r = yield [
|
||||
packageService.listModulesByName(name),
|
||||
packageService.listStarUserNames(name),
|
||||
@@ -77,18 +124,18 @@ module.exports = function* list() {
|
||||
|
||||
if (rows.length === 0) {
|
||||
// check if unpublished
|
||||
var unpublishedInfo = yield* packageService.getUnpublishedModule(name);
|
||||
var unpublishedInfo = yield packageService.getUnpublishedModule(name);
|
||||
debug('show unpublished %j', unpublishedInfo);
|
||||
if (unpublishedInfo) {
|
||||
this.status = 404;
|
||||
this.body = {
|
||||
_id: orginalName,
|
||||
name: orginalName,
|
||||
this.jsonp = {
|
||||
_id: name,
|
||||
name: name,
|
||||
time: {
|
||||
modified: unpublishedInfo.package.time,
|
||||
unpublished: unpublishedInfo.package,
|
||||
},
|
||||
_attachments: {}
|
||||
_attachments: {},
|
||||
};
|
||||
return;
|
||||
}
|
||||
@@ -99,15 +146,16 @@ module.exports = function* list() {
|
||||
if (rows.length === 0) {
|
||||
if (!this.allowSync) {
|
||||
this.status = 404;
|
||||
this.body = {
|
||||
error: 'not_found',
|
||||
reason: 'document not found'
|
||||
const error = '[not_found] document not found';
|
||||
this.jsonp = {
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
// start sync
|
||||
var logId = yield* SyncModuleWorker.sync(name, 'sync-by-install');
|
||||
var logId = yield SyncModuleWorker.sync(name, 'sync-by-install');
|
||||
debug('start sync %s, get log id %s', name, logId);
|
||||
|
||||
return this.redirect(config.officialNpmRegistry + this.url);
|
||||
@@ -124,15 +172,23 @@ module.exports = function* list() {
|
||||
|
||||
// set versions and times
|
||||
var versions = {};
|
||||
var allVersionString = '';
|
||||
var times = {};
|
||||
var attachments = {};
|
||||
var createdTime = null;
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var row = rows[i];
|
||||
var pkg = row.package;
|
||||
// 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) {
|
||||
@@ -174,12 +230,19 @@ module.exports = function* list() {
|
||||
distTags.latest = pkg.version;
|
||||
}
|
||||
|
||||
if (!readme && config.enableAbbreviatedMetadata) {
|
||||
var packageReadme = yield packageService.getPackageReadme(name);
|
||||
if (packageReadme) {
|
||||
readme = packageReadme.readme;
|
||||
}
|
||||
}
|
||||
|
||||
var info = {
|
||||
_id: orginalName,
|
||||
_id: name,
|
||||
_rev: rev,
|
||||
name: orginalName,
|
||||
name: name,
|
||||
description: pkg.description,
|
||||
"dist-tags": distTags,
|
||||
'dist-tags': distTags,
|
||||
maintainers: pkg.maintainers,
|
||||
time: times,
|
||||
users: starUsers,
|
||||
@@ -195,6 +258,194 @@ module.exports = function* list() {
|
||||
info.bugs = pkg.bugs;
|
||||
info.license = pkg.license;
|
||||
|
||||
debug('show module %s: %s, latest: %s', orginalName, rev, latestMod.version);
|
||||
this.body = info;
|
||||
if (typeof config.formatCustomFullPackageInfoAndVersions === 'function') {
|
||||
info = config.formatCustomFullPackageInfoAndVersions(this, info);
|
||||
}
|
||||
|
||||
debug('show module %s: %s, latest: %s', name, rev, latestMod.version);
|
||||
this.jsonp = info;
|
||||
// use faster etag
|
||||
this.etag = etag([
|
||||
modifiedTime,
|
||||
distTags,
|
||||
pkg.maintainers,
|
||||
allVersionString,
|
||||
]);
|
||||
|
||||
if (config.registryCacheControlHeader) {
|
||||
this.set('cache-control', config.registryCacheControlHeader);
|
||||
}
|
||||
if (config.registryVaryHeader) {
|
||||
this.set('vary', config.registryVaryHeader);
|
||||
}
|
||||
};
|
||||
|
||||
function* handleAbbreviatedMetaRequest(ctx, name, modifiedTime, tags, rows, cacheKey) {
|
||||
debug('show %s got %d rows, %d tags, modifiedTime: %s', name, rows.length, tags.length, modifiedTime);
|
||||
const isJSONPRequest = ctx.query.callback;
|
||||
var latestMod = null;
|
||||
// set tags
|
||||
var distTags = {};
|
||||
for (var i = 0; i < tags.length; i++) {
|
||||
var t = tags[i];
|
||||
distTags[t.tag] = t.version;
|
||||
}
|
||||
|
||||
// set versions and times
|
||||
var versions = {};
|
||||
var allVersionString = '';
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var row = rows[i];
|
||||
var pkg = row.package;
|
||||
common.setDownloadURL(pkg, ctx);
|
||||
pkg._publish_on_cnpm = undefined;
|
||||
pkg.publish_time = pkg.publish_time || row.publish_time;
|
||||
|
||||
versions[pkg.version] = pkg;
|
||||
allVersionString += pkg.version + ',';
|
||||
|
||||
if ((!distTags.latest && !latestMod) || distTags.latest === pkg.version) {
|
||||
latestMod = row;
|
||||
}
|
||||
// abbreviatedMeta row maybe update by syncer on missing attributes add
|
||||
if (!modifiedTime || row.gmt_modified > modifiedTime) {
|
||||
modifiedTime = row.gmt_modified;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
peerDependenciesMeta: row.package.peerDependenciesMeta,
|
||||
bin: row.package.bin,
|
||||
os: row.package.os,
|
||||
cpu: row.package.cpu,
|
||||
directories: row.package.directories,
|
||||
dist: row.package.dist,
|
||||
engines: row.package.engines,
|
||||
workspaces: row.package.workspaces,
|
||||
_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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/package/list_all.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var packageService = require('../../../services/package');
|
||||
|
||||
// GET /-/all
|
||||
@@ -21,7 +7,7 @@ var packageService = require('../../../services/package');
|
||||
// https://github.com/npm/npm-registry-client/blob/master/lib/get.js#L86
|
||||
module.exports = function* () {
|
||||
var updated = Date.now();
|
||||
var names = yield* packageService.listAllPublicModuleNames();
|
||||
var names = yield packageService.listAllPublicModuleNames();
|
||||
var result = { _updated: updated };
|
||||
names.forEach(function (name) {
|
||||
result[name] = true;
|
||||
|
||||
26
controllers/registry/package/list_dependents.js
Normal file
26
controllers/registry/package/list_dependents.js
Normal 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,
|
||||
};
|
||||
};
|
||||
@@ -1,23 +1,32 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/package/list_shorts.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var packageService = require('../../../services/package');
|
||||
const packageService = require('../../../services/package');
|
||||
const config = require('../../../config');
|
||||
|
||||
// GET /-/short
|
||||
// List all packages names only
|
||||
// List public all packages names only
|
||||
module.exports = function* () {
|
||||
this.body = yield* packageService.listAllPublicModuleNames();
|
||||
if (this.query.private_only) {
|
||||
const tasks = [];
|
||||
for (let i = 0; i < config.scopes.length; i++) {
|
||||
const scope = config.scopes[i];
|
||||
tasks.push(packageService.listPrivateModulesByScope(scope));
|
||||
}
|
||||
|
||||
if (config.privatePackages && config.privatePackages.length > 0) {
|
||||
tasks.push(packageService.listModules(config.privatePackages));
|
||||
}
|
||||
|
||||
const results = yield tasks;
|
||||
const names = [];
|
||||
for (const rows of results) {
|
||||
for (const row of rows) {
|
||||
names.push(row.name);
|
||||
}
|
||||
}
|
||||
this.body = names;
|
||||
return;
|
||||
}
|
||||
|
||||
this.body = yield packageService.listAllPublicModuleNames();
|
||||
};
|
||||
|
||||
@@ -1,33 +1,21 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/package/list_since.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var packageService = require('../../../services/package');
|
||||
|
||||
var A_WEEK_MS = 3600000 * 24 * 7;
|
||||
var TWA_DAYS_MS = 3600000 * 24 * 2;
|
||||
|
||||
// GET /-/all/since?stale=update_after&startkey={key}
|
||||
// List packages names since startkey
|
||||
// https://github.com/npm/npm-registry-client/blob/master/lib/get.js#L89
|
||||
module.exports = function* () {
|
||||
module.exports = function* listSince() {
|
||||
var query = this.query;
|
||||
if (query.stale !== 'update_after') {
|
||||
this.status = 400;
|
||||
const error = '[query_parse_error] Invalid value for `stale`.';
|
||||
this.body = {
|
||||
error: 'query_parse_error',
|
||||
reason: 'Invalid value for `stale`.'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
@@ -35,21 +23,22 @@ module.exports = function* () {
|
||||
var startkey = Number(query.startkey);
|
||||
if (!startkey) {
|
||||
this.status = 400;
|
||||
const error = '[query_parse_error] Invalid value for `startkey`.';
|
||||
this.body = {
|
||||
error: 'query_parse_error',
|
||||
reason: 'Invalid value for `startkey`.'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
var updated = Date.now();
|
||||
if (updated - startkey > A_WEEK_MS) {
|
||||
startkey = updated - A_WEEK_MS;
|
||||
console.warn('[%s] list modules since time out of range: query: %j, ip: %s',
|
||||
Date(), query, this.ip);
|
||||
startkey = updated - TWA_DAYS_MS;
|
||||
console.warn('[%s] list modules since time out of range: query: %j, ip: %s, limit to %s',
|
||||
Date(), query, this.ip, startkey);
|
||||
}
|
||||
|
||||
var names = yield* packageService.listPublicModuleNamesSince(startkey);
|
||||
var names = yield packageService.listPublicModuleNamesSince(startkey);
|
||||
var result = { _updated: updated };
|
||||
names.forEach(function (name) {
|
||||
result[name] = true;
|
||||
|
||||
37
controllers/registry/package/list_versions.js
Normal file
37
controllers/registry/package/list_versions.js
Normal 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,
|
||||
};
|
||||
});
|
||||
};
|
||||
@@ -1,25 +1,12 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/package/remove.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:registry:package:remove');
|
||||
var urlparse = require('url').parse;
|
||||
var packageService = require('../../../services/package');
|
||||
var totalService = require('../../../services/total');
|
||||
var nfs = require('../../../common/nfs');
|
||||
var logger = require('../../../common/logger');
|
||||
var config = require('../../../config');
|
||||
|
||||
// DELETE /:name/-rev/:rev
|
||||
// https://github.com/npm/npm-registry-client/blob/master/lib/unpublish.js#L25
|
||||
@@ -28,11 +15,11 @@ module.exports = function* remove(next) {
|
||||
var rev = this.params.rev || this.params[1];
|
||||
debug('remove all the module with name: %s, id: %s', name, rev);
|
||||
|
||||
var mods = yield* packageService.listModulesByName(name);
|
||||
var mods = yield packageService.listModulesByName(name);
|
||||
debug('removeAll module %s: %d', name, mods.length);
|
||||
var mod = mods[0];
|
||||
if (!mod) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
|
||||
yield [
|
||||
@@ -41,27 +28,29 @@ module.exports = function* remove(next) {
|
||||
totalService.plusDeleteModule(),
|
||||
];
|
||||
|
||||
var keys = [];
|
||||
for (var i = 0; i < mods.length; i++) {
|
||||
var row = mods[i];
|
||||
var dist = row.package.dist;
|
||||
var key = dist.key;
|
||||
if (!key) {
|
||||
key = urlparse(dist.tarball).pathname;
|
||||
if (config.unpublishRemoveTarball) {
|
||||
var keys = [];
|
||||
for (var i = 0; i < mods.length; i++) {
|
||||
var row = mods[i];
|
||||
var dist = row.package.dist;
|
||||
var key = dist.key;
|
||||
if (!key) {
|
||||
key = urlparse(dist.tarball).pathname;
|
||||
}
|
||||
key && keys.push(key);
|
||||
}
|
||||
key && keys.push(key);
|
||||
}
|
||||
|
||||
try {
|
||||
yield keys.map(function (key) {
|
||||
return nfs.remove(key);
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
try {
|
||||
yield keys.map(function (key) {
|
||||
return nfs.remove(key);
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
// remove the maintainers
|
||||
yield* packageService.removeAllMaintainers(name);
|
||||
yield packageService.removeAllMaintainers(name);
|
||||
|
||||
this.body = { ok: true };
|
||||
};
|
||||
|
||||
@@ -1,24 +1,11 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/package/remove_version.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:registry:package:remove_version');
|
||||
var packageService = require('../../../services/package');
|
||||
var nfs = require('../../../common/nfs');
|
||||
var logger = require('../../../common/logger');
|
||||
var getCDNKey = require('../../../lib/common').getCDNKey;
|
||||
var config = require('../../../config');
|
||||
|
||||
// DELETE /:name/download/:filename/-rev/:rev
|
||||
// https://github.com/npm/npm-registry-client/blob/master/lib/unpublish.js#L97
|
||||
@@ -33,13 +20,13 @@ module.exports = function* removeOneVersion(next) {
|
||||
version = version.substring(0, version.lastIndexOf('.tgz'));
|
||||
}
|
||||
if (!version) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
|
||||
debug('remove tarball with filename: %s, version: %s, revert to => rev id: %s', filename, version, id);
|
||||
|
||||
if (isNaN(id)) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
|
||||
var rs = yield [
|
||||
@@ -49,26 +36,30 @@ module.exports = function* removeOneVersion(next) {
|
||||
var revertTo = rs[0];
|
||||
var mod = rs[1]; // module need to delete
|
||||
if (!mod || mod.name !== name) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
|
||||
var key = mod.package && mod.package.dist && mod.package.dist.key;
|
||||
if (!key) {
|
||||
key = getCDNKey(mod.name, filename);
|
||||
if (config.unpublishRemoveTarball) {
|
||||
var key = mod.package && mod.package.dist && mod.package.dist.key;
|
||||
if (!key) {
|
||||
key = getCDNKey(mod.name, filename);
|
||||
}
|
||||
|
||||
if (revertTo && revertTo.package) {
|
||||
debug('removing key: %s from nfs, revert to %s@%s', key, revertTo.name, revertTo.package.version);
|
||||
} else {
|
||||
debug('removing key: %s from nfs, no revert mod', key);
|
||||
}
|
||||
|
||||
try {
|
||||
yield nfs.remove(key);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (revertTo && revertTo.package) {
|
||||
debug('removing key: %s from nfs, revert to %s@%s', key, revertTo.name, revertTo.package.version);
|
||||
} else {
|
||||
debug('removing key: %s from nfs, no revert mod', key);
|
||||
}
|
||||
try {
|
||||
yield nfs.remove(key);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
}
|
||||
// remove version from table
|
||||
yield* packageService.removeModulesByNameAndVersions(name, [version]);
|
||||
yield packageService.removeModulesByNameAndVersions(name, [version]);
|
||||
debug('removed %s@%s', name, version);
|
||||
this.body = { ok: true };
|
||||
};
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/package/save.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:registry:package:save');
|
||||
var crypto = require('crypto');
|
||||
var deprecateVersions = require('./deprecate');
|
||||
@@ -21,6 +7,7 @@ var packageService = require('../../../services/package');
|
||||
var common = require('../../../lib/common');
|
||||
var nfs = require('../../../common/nfs');
|
||||
var config = require('../../../config');
|
||||
var hook = require('../../../services/hook');
|
||||
|
||||
// old flows:
|
||||
// 1. add()
|
||||
@@ -44,21 +31,23 @@ module.exports = function* save(next) {
|
||||
var version = Object.keys(pkg.versions || {})[0];
|
||||
if (!version) {
|
||||
this.status = 400;
|
||||
const error = '[version_error] package.versions is empty';
|
||||
this.body = {
|
||||
error: 'version_error',
|
||||
reason: 'package.versions is empty'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
// check maintainers
|
||||
var result = yield* packageService.authMaintainer(name, username);
|
||||
var result = yield packageService.authMaintainer(name, username);
|
||||
if (!result.isMaintainer) {
|
||||
this.status = 403;
|
||||
const error = '[forbidden] ' + username + ' not authorized to modify ' + name +
|
||||
', please contact maintainers: ' + result.maintainers.join(', ');
|
||||
this.body = {
|
||||
error: 'forbidden user',
|
||||
reason: username + ' not authorized to modify ' + name +
|
||||
', please contact maintainers: ' + result.maintainers.join(', ')
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
@@ -73,13 +62,14 @@ module.exports = function* save(next) {
|
||||
}
|
||||
}
|
||||
if (hasDeprecated) {
|
||||
return yield* deprecateVersions.call(this, next);
|
||||
return yield deprecateVersions.call(this, next);
|
||||
}
|
||||
|
||||
this.status = 400;
|
||||
const error = '[attachment_error] package._attachments is empty';
|
||||
this.body = {
|
||||
error: 'attachment_error',
|
||||
reason: 'package._attachments is empty'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
@@ -88,29 +78,52 @@ module.exports = function* save(next) {
|
||||
var versionPackage = pkg.versions[version];
|
||||
var maintainers = versionPackage.maintainers;
|
||||
|
||||
// should never happened in normal request
|
||||
var authorizeType = common.getAuthorizeType(this);
|
||||
if (!maintainers) {
|
||||
this.status = 400;
|
||||
this.body = {
|
||||
error: 'maintainers error',
|
||||
reason: 'request body need maintainers'
|
||||
};
|
||||
return;
|
||||
if (authorizeType === common.AuthorizeType.BEARER) {
|
||||
// With the token mode, pub lib with no maintainers
|
||||
// make the maintainer to be puber
|
||||
maintainers = [{
|
||||
name: this.user.name,
|
||||
email: this.user.email,
|
||||
}];
|
||||
} else {
|
||||
// should never happened in normal request
|
||||
this.status = 400;
|
||||
const error = '[maintainers_error] request body need maintainers';
|
||||
this.body = {
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// notice that admins can not publish to all modules
|
||||
// (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;
|
||||
});
|
||||
|
||||
// package.json has maintainers and publisher in not in the list
|
||||
if (authorizeType === common.AuthorizeType.BEARER && m.length === 0) {
|
||||
var publisher = {
|
||||
name: this.user.name,
|
||||
email: this.user.email,
|
||||
};
|
||||
m = [ publisher ];
|
||||
maintainers.push(publisher);
|
||||
}
|
||||
|
||||
// make sure user in auth is in maintainers
|
||||
// should never happened in normal request
|
||||
if (m.length === 0) {
|
||||
this.status = 403;
|
||||
const error = '[maintainers_error] ' + username + ' does not in maintainer list';
|
||||
this.body = {
|
||||
error: 'maintainers error',
|
||||
reason: username + ' does not in maintainer list'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
@@ -125,9 +138,10 @@ module.exports = function* save(next) {
|
||||
|
||||
if (tags.length === 0) {
|
||||
this.status = 400;
|
||||
const error = '[invalid] dist-tags should not be empty';
|
||||
this.body = {
|
||||
error: 'invalid',
|
||||
reason: 'dist-tags should not be empty'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
@@ -135,34 +149,36 @@ module.exports = function* save(next) {
|
||||
debug('%s publish new %s:%s, attachment size: %s, maintainers: %j, distTags: %j',
|
||||
username, name, version, attachment.length, versionPackage.maintainers, distTags);
|
||||
|
||||
var exists = yield* packageService.getModule(name, version);
|
||||
var exists = yield packageService.getModule(name, version);
|
||||
var shasum;
|
||||
if (exists) {
|
||||
this.status = 403;
|
||||
const error = '[forbidden] cannot modify pre-existing version: ' + version;
|
||||
this.body = {
|
||||
error: 'forbidden',
|
||||
reason: 'cannot modify pre-existing version: ' + version
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
// upload attachment
|
||||
var tarballBuffer;
|
||||
tarballBuffer = new Buffer(attachment.data, 'base64');
|
||||
tarballBuffer = Buffer.from(attachment.data, 'base64');
|
||||
|
||||
if (tarballBuffer.length !== attachment.length) {
|
||||
this.status = 403;
|
||||
const error = '[size_wrong] Attachment size ' + attachment.length
|
||||
+ ' not match download size ' + tarballBuffer.length;
|
||||
this.body = {
|
||||
error: 'size_wrong',
|
||||
reason: 'Attachment size ' + attachment.length
|
||||
+ ' not match download size ' + tarballBuffer.length,
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if (!distTags.latest) {
|
||||
// need to check if latest tag exists or not
|
||||
var latest = yield* packageService.getModuleByTag(name, 'latest');
|
||||
var latest = yield packageService.getModuleByTag(name, 'latest');
|
||||
if (!latest) {
|
||||
// auto add latest
|
||||
tags.push(['latest', tags[0][1]]);
|
||||
@@ -202,9 +218,9 @@ module.exports = function* save(next) {
|
||||
};
|
||||
|
||||
mod.package.dist = dist;
|
||||
yield* addDepsRelations(mod.package);
|
||||
yield addDepsRelations(mod.package);
|
||||
|
||||
var addResult = yield* packageService.saveModule(mod);
|
||||
var addResult = yield packageService.saveModule(mod);
|
||||
debug('%s module: save file to %s, size: %d, sha1: %s, dist: %j, version: %s',
|
||||
addResult.id, dist.tarball, dist.size, shasum, dist, version);
|
||||
|
||||
@@ -219,13 +235,25 @@ module.exports = function* save(next) {
|
||||
var maintainerNames = maintainers.map(function (item) {
|
||||
return item.name;
|
||||
});
|
||||
yield* packageService.addPrivateModuleMaintainers(name, maintainerNames);
|
||||
yield packageService.addPrivateModuleMaintainers(name, maintainerNames);
|
||||
|
||||
this.status = 201;
|
||||
this.body = {
|
||||
ok: true,
|
||||
rev: String(addResult.id)
|
||||
};
|
||||
|
||||
// hooks
|
||||
const envelope = {
|
||||
event: 'package:publish',
|
||||
name: mod.name,
|
||||
type: 'package',
|
||||
version: mod.version,
|
||||
hookOwner: null,
|
||||
payload: null,
|
||||
change: null,
|
||||
};
|
||||
hook.trigger(envelope);
|
||||
};
|
||||
|
||||
function* addDepsRelations(pkg) {
|
||||
|
||||
@@ -1,23 +1,7 @@
|
||||
/**!
|
||||
* cnpmjs.js - controllers/registry/package/show.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:registry:package:show');
|
||||
var semver = require('semver');
|
||||
var packageService = require('../../../services/package');
|
||||
var setDownloadURL = require('../../../lib/common').setDownloadURL;
|
||||
var SyncModuleWorker = require('../../sync_module_worker');
|
||||
var config = require('../../../config');
|
||||
|
||||
@@ -32,36 +16,36 @@ var config = require('../../../config');
|
||||
module.exports = function* show() {
|
||||
var name = this.params.name || this.params[0];
|
||||
var tag = this.params.version || this.params[1];
|
||||
var version = semver.valid(tag);
|
||||
var mod;
|
||||
if (version) {
|
||||
mod = yield* packageService.getModule(name, version);
|
||||
} else {
|
||||
mod = yield* packageService.getModuleByTag(name, tag);
|
||||
}
|
||||
var mod = yield packageService.showPackage(name, tag, this);
|
||||
|
||||
if (mod) {
|
||||
setDownloadURL(mod.package, this);
|
||||
mod.package._cnpm_publish_time = mod.publish_time;
|
||||
var maintainers = yield* packageService.listMaintainers(name);
|
||||
if (maintainers.length > 0) {
|
||||
mod.package.maintainers = maintainers;
|
||||
if (typeof config.formatCustomOnePackageVersion === 'function') {
|
||||
mod.package = config.formatCustomOnePackageVersion(this, mod.package);
|
||||
}
|
||||
|
||||
this.jsonp = mod.package;
|
||||
if (config.registryCacheControlHeader) {
|
||||
this.set('cache-control', config.registryCacheControlHeader);
|
||||
}
|
||||
if (config.registryVaryHeader) {
|
||||
this.set('vary', config.registryVaryHeader);
|
||||
}
|
||||
this.body = mod.package;
|
||||
return;
|
||||
}
|
||||
|
||||
// if not fond, sync from source registry
|
||||
if (!this.allowSync) {
|
||||
this.status = 404;
|
||||
this.body = {
|
||||
error: 'not exist',
|
||||
reason: 'version not found: ' + version
|
||||
const error = '[not_exists] version not found: ' + tag;
|
||||
this.jsonp = {
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
// start sync
|
||||
var logId = yield* SyncModuleWorker.sync(name, 'sync-by-install');
|
||||
var logId = yield SyncModuleWorker.sync(name, 'sync-by-install');
|
||||
debug('start sync %s, get log id %s', name, logId);
|
||||
|
||||
this.redirect(config.officialNpmRegistry + this.url);
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/package/tag.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:registry:package:tag');
|
||||
var semver = require('semver');
|
||||
var util = require('util');
|
||||
@@ -30,40 +16,41 @@ module.exports = function* tag() {
|
||||
|
||||
if (!version) {
|
||||
this.status = 400;
|
||||
const error = '[version_missed] version not found';
|
||||
this.body = {
|
||||
error: 'version_missed',
|
||||
reason: 'version not found'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if (!semver.valid(version)) {
|
||||
this.status = 403;
|
||||
var reason = util.format('setting tag %s to invalid version: %s: %s/%s',
|
||||
const error = util.format('[forbidden] setting tag %s to invalid version: %s: %s/%s',
|
||||
tag, version, name, tag);
|
||||
this.body = {
|
||||
error: 'forbidden',
|
||||
reason: reason
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
var mod = yield* packageService.getModule(name, version);
|
||||
var mod = yield packageService.getModule(name, version);
|
||||
if (!mod) {
|
||||
this.status = 403;
|
||||
var reason = util.format('setting tag %s to unknown version: %s: %s/%s',
|
||||
const error = util.format('[forbidden] setting tag %s to unknown version: %s: %s/%s',
|
||||
tag, version, name, tag);
|
||||
this.body = {
|
||||
error: 'forbidden',
|
||||
reason: reason
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
var row = yield* packageService.addModuleTag(name, tag, version);
|
||||
var row = yield packageService.addModuleTag(name, tag, version);
|
||||
this.status = 201;
|
||||
this.body = {
|
||||
ok: true,
|
||||
modified: row.gmt_modified
|
||||
modified: row.gmt_modified,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,23 +1,10 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/package/update.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:registry:package:update');
|
||||
var packageService = require('../../../services/package');
|
||||
var userService = require('../../../services/user');
|
||||
var config = require('../../../config');
|
||||
var hook = require('../../../services/hook');
|
||||
|
||||
// PUT /:name/-rev/:rev
|
||||
//
|
||||
@@ -29,11 +16,11 @@ module.exports = function* update(next) {
|
||||
|
||||
var body = this.request.body;
|
||||
if (body.versions) {
|
||||
yield* updateVersions.call(this, next);
|
||||
yield updateVersions.call(this, next);
|
||||
} else if (body.maintainers) {
|
||||
yield* updateMaintainers.call(this, next);
|
||||
yield updateMaintainers.call(this, next);
|
||||
} else {
|
||||
yield* next;
|
||||
yield next;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -45,11 +32,11 @@ function* updateVersions(next) {
|
||||
var versions = this.request.body.versions;
|
||||
|
||||
// step1: list all the versions
|
||||
var mods = yield* packageService.listModulesByName(name);
|
||||
var mods = yield packageService.listModulesByName(name);
|
||||
debug('removeWithVersions module %s, left versions %j, %s mods',
|
||||
name, Object.keys(versions), mods && mods.length);
|
||||
if (!mods || !mods.length) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
|
||||
// step3: calculate which versions need to remove and
|
||||
@@ -78,7 +65,7 @@ function* updateVersions(next) {
|
||||
|
||||
// step 4: remove all the versions which need to remove
|
||||
// let removeTar do remove versions from module table
|
||||
var tags = yield* packageService.listModuleTags(name);
|
||||
var tags = yield packageService.listModuleTags(name);
|
||||
|
||||
var removeTags = [];
|
||||
var latestRemoved = false;
|
||||
@@ -95,17 +82,17 @@ function* updateVersions(next) {
|
||||
debug('remove tags: %j', removeTags);
|
||||
if (removeTags.length) {
|
||||
// step 5: remove all the tags
|
||||
yield* packageService.removeModuleTagsByIds(removeTags);
|
||||
yield packageService.removeModuleTagsByIds(removeTags);
|
||||
if (latestRemoved && remainVersions[0]) {
|
||||
debug('latest tags removed, generate a new latest tag with new version: %s',
|
||||
remainVersions[0]);
|
||||
// step 6: insert new latest tag
|
||||
yield* packageService.addModuleTag(name, 'latest', remainVersions[0]);
|
||||
yield packageService.addModuleTag(name, 'latest', remainVersions[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// step 7: update last modified, make sure etag change
|
||||
yield* packageService.updateModuleLastModified(name);
|
||||
yield packageService.updateModuleLastModified(name);
|
||||
|
||||
this.status = 201;
|
||||
this.body = { ok: true };
|
||||
@@ -122,16 +109,17 @@ function* updateMaintainers() {
|
||||
|
||||
if (usernames.length === 0) {
|
||||
this.status = 403;
|
||||
const error = '[invalid_operation] Can not remove all maintainers';
|
||||
this.body = {
|
||||
error: 'invalid operation',
|
||||
reason: 'Can not remove all maintainers'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.customUserService) {
|
||||
// ensure new authors are vaild
|
||||
var maintainers = yield* packageService.listMaintainerNamesOnly(name);
|
||||
var maintainers = yield packageService.listMaintainerNamesOnly(name);
|
||||
var map = {};
|
||||
var newNames = [];
|
||||
for (var i = 0; i < maintainers.length; i++) {
|
||||
@@ -144,7 +132,7 @@ function* updateMaintainers() {
|
||||
}
|
||||
}
|
||||
if (newNames.length > 0) {
|
||||
var users = yield* userService.list(newNames);
|
||||
var users = yield userService.list(newNames);
|
||||
var map = {};
|
||||
for (var i = 0; i < users.length; i++) {
|
||||
var user = users[i];
|
||||
@@ -159,17 +147,43 @@ function* updateMaintainers() {
|
||||
}
|
||||
if (invailds.length > 0) {
|
||||
this.status = 403;
|
||||
const error = '[invalid] User: `' + invailds.join(', ') + '` not exists';
|
||||
this.body = {
|
||||
error: 'invalid user name',
|
||||
reason: 'User: `' + invailds.join(', ') + '` not exists'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var r = yield* packageService.updatePrivateModuleMaintainers(name, usernames);
|
||||
var r = yield packageService.updatePrivateModuleMaintainers(name, usernames);
|
||||
debug('result: %j', r);
|
||||
if (r.add && r.add.length) {
|
||||
const envelope = {
|
||||
event: 'package:owner',
|
||||
name: name,
|
||||
type: 'package',
|
||||
version: null,
|
||||
hookOwner: null,
|
||||
payload: null,
|
||||
change: null,
|
||||
};
|
||||
hook.trigger(envelope);
|
||||
}
|
||||
if (r.remove && r.remove.length) {
|
||||
const envelope = {
|
||||
event: 'package:owner-rm',
|
||||
name: name,
|
||||
type: 'package',
|
||||
version: null,
|
||||
hookOwner: null,
|
||||
payload: null,
|
||||
change: null,
|
||||
};
|
||||
hook.trigger(envelope);
|
||||
}
|
||||
|
||||
|
||||
this.status = 201;
|
||||
this.body = {
|
||||
|
||||
55
controllers/registry/token/create.js
Normal file
55
controllers/registry/token/create.js
Normal file
@@ -0,0 +1,55 @@
|
||||
'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 tokenServiceImpl = this.tokenService || tokenService;
|
||||
|
||||
var token = yield tokenServiceImpl.createToken(this.user.name, {
|
||||
readonly: !!readonly,
|
||||
cidrWhitelist: cidrWhitelist || [],
|
||||
});
|
||||
this.status = 201;
|
||||
this.body = token;
|
||||
};
|
||||
8
controllers/registry/token/del.js
Normal file
8
controllers/registry/token/del.js
Normal 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;
|
||||
};
|
||||
60
controllers/registry/token/list.js
Normal file
60
controllers/registry/token/list.js
Normal 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: {},
|
||||
};
|
||||
};
|
||||
@@ -1,23 +1,9 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/user/add.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var ensurePasswordSalt = require('./common').ensurePasswordSalt;
|
||||
var userService = require('../../../services/user');
|
||||
var config = require('../../../config');
|
||||
var tokenService = require('../../../services/token');
|
||||
|
||||
// npm 1.4.4
|
||||
// add new user first
|
||||
@@ -55,6 +41,54 @@ var config = require('../../../config');
|
||||
module.exports = function* addUser() {
|
||||
var name = this.params.name;
|
||||
var body = this.request.body || {};
|
||||
|
||||
if (!body.password || !body.name) {
|
||||
this.status = 422;
|
||||
const error = '[param_error] params missing, name, email or password missing';
|
||||
this.body = {
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
var loginedUser;
|
||||
try {
|
||||
loginedUser = yield userService.authAndSave(body.name, body.password);
|
||||
} catch (err) {
|
||||
this.status = err.status || 500;
|
||||
this.body = {
|
||||
error: err.message,
|
||||
reason: err.message,
|
||||
};
|
||||
return;
|
||||
}
|
||||
if (loginedUser) {
|
||||
var token = yield tokenService.createToken(body.name, {
|
||||
readonly: !!body.readonly,
|
||||
cidrWhitelist: body.cidr_whitelist || [],
|
||||
});
|
||||
this.status = 201;
|
||||
this.body = {
|
||||
token: token.token,
|
||||
ok: true,
|
||||
id: 'org.couchdb.user:' + loginedUser.login,
|
||||
rev: Date.now() + '-' + loginedUser.login
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.customUserService) {
|
||||
// user login fail, not allow to add new user
|
||||
this.status = 401;
|
||||
const error = '[unauthorized] Login fail, please check your login name and password';
|
||||
this.body = {
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
var user = {
|
||||
name: body.name,
|
||||
// salt: body.salt,
|
||||
@@ -66,61 +100,39 @@ module.exports = function* addUser() {
|
||||
|
||||
ensurePasswordSalt(user, body);
|
||||
|
||||
if (!body.password || !user.name || !user.salt || !user.password_sha || !user.email) {
|
||||
if (!user.salt || !user.password_sha || !user.email) {
|
||||
this.status = 422;
|
||||
const error = '[param_error] params missing, name, email or password missing';
|
||||
this.body = {
|
||||
error: 'paramError',
|
||||
reason: 'params missing, name, email or password missing.'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
var loginedUser;
|
||||
try {
|
||||
loginedUser = yield* userService.authAndSave(body.name, body.password);
|
||||
} catch (err) {
|
||||
this.status = err.status || 500;
|
||||
this.body = {
|
||||
error: err.name,
|
||||
reason: err.message
|
||||
};
|
||||
return;
|
||||
}
|
||||
if (loginedUser) {
|
||||
this.status = 201;
|
||||
this.body = {
|
||||
ok: true,
|
||||
id: 'org.couchdb.user:' + loginedUser.login,
|
||||
rev: Date.now() + '-' + loginedUser.login
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.customUserService) {
|
||||
// user login fail, not allow to add new user
|
||||
this.status = 401;
|
||||
this.body = {
|
||||
error: 'unauthorized',
|
||||
reason: 'Login fail, please check your login name and password'
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
var existUser = yield* userService.get(name);
|
||||
var existUser = yield userService.get(name);
|
||||
if (existUser) {
|
||||
this.status = 409;
|
||||
const error = '[conflict] User ' + name + ' already exists';
|
||||
this.body = {
|
||||
error: 'conflict',
|
||||
reason: 'User ' + name + ' already exists.'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
// add new user
|
||||
var result = yield* userService.add(user);
|
||||
var result = yield userService.add(user);
|
||||
this.etag = '"' + result.rev + '"';
|
||||
|
||||
var token = yield tokenService.createToken(body.name, {
|
||||
readonly: !!body.readonly,
|
||||
cidrWhitelist: body.cidr_whitelist || [],
|
||||
});
|
||||
|
||||
this.status = 201;
|
||||
this.body = {
|
||||
token: token.token,
|
||||
ok: true,
|
||||
id: 'org.couchdb.user:' + name,
|
||||
rev: result.rev
|
||||
|
||||
7
controllers/registry/user/ping.js
Normal file
7
controllers/registry/user/ping.js
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
// https://docs.npmjs.com/cli/ping
|
||||
module.exports = function* () {
|
||||
this.status = 200;
|
||||
this.body = {};
|
||||
};
|
||||
@@ -1,28 +1,13 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/user/show.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var userService = require('../../../services/user');
|
||||
|
||||
// GET /-/user/org.couchdb.user::name
|
||||
module.exports = function* show(next) {
|
||||
var name = this.params.name;
|
||||
var user = yield* userService.getAndSave(name);
|
||||
var user = yield userService.getAndSave(name);
|
||||
if (!user) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
|
||||
var data = user.json;
|
||||
|
||||
@@ -1,20 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/user/update.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:registry:user:update');
|
||||
var ensurePasswordSalt = require('./common').ensurePasswordSalt;
|
||||
var userService = require('../../../services/user');
|
||||
@@ -40,16 +25,17 @@ module.exports = function* updateUser(next) {
|
||||
var name = this.params.name;
|
||||
var rev = this.params.rev;
|
||||
if (!name || !rev) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
debug('update: %s, rev: %s, user.name: %s', name, rev, this.user.name);
|
||||
|
||||
if (name !== this.user.name) {
|
||||
// must auth user first
|
||||
this.status = 401;
|
||||
const error = '[unauthorized] Name is incorrect';
|
||||
this.body = {
|
||||
error: 'unauthorized',
|
||||
reason: 'Name is incorrect.'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
@@ -71,19 +57,21 @@ module.exports = function* updateUser(next) {
|
||||
|
||||
if (!body.password || !user.name || !user.salt || !user.password_sha || !user.email) {
|
||||
this.status = 422;
|
||||
const error = '[param_error] params missing, name, email or password missing';
|
||||
this.body = {
|
||||
error: 'paramError',
|
||||
reason: 'params missing, name, email or password missing.'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
var result = yield* userService.update(user);
|
||||
var result = yield userService.update(user);
|
||||
if (!result) {
|
||||
this.status = 409;
|
||||
const error = '[conflict] Document update conflict';
|
||||
this.body = {
|
||||
error: 'conflict',
|
||||
reason: 'Document update conflict.'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
9
controllers/registry/user/whoami.js
Normal file
9
controllers/registry/user/whoami.js
Normal 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,
|
||||
};
|
||||
};
|
||||
@@ -21,9 +21,10 @@ exports.list = function* () {
|
||||
var users = this.params.user.split('|');
|
||||
if (users.length > 20) {
|
||||
this.status = 400;
|
||||
const error = '[bad_request] reach max user names limit, must <= 20 user names';
|
||||
this.body = {
|
||||
error: 'bad_request',
|
||||
reason: 'reach max user names limit, must <= 20 user names'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/sync.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:sync');
|
||||
var Log = require('../services/module_log');
|
||||
var SyncModuleWorker = require('./sync_module_worker');
|
||||
@@ -32,12 +18,18 @@ exports.sync = function* () {
|
||||
}
|
||||
var publish = this.query.publish === 'true';
|
||||
var noDep = this.query.nodeps === 'true';
|
||||
debug('sync %s with query: %j', name, this.query);
|
||||
var syncUpstreamFirst = this.query.sync_upstream === 'true';
|
||||
var syncFromBackupFile = this.query.sync_from_backup === 'true';
|
||||
if (!config.sourceNpmRegistryIsCNpm) {
|
||||
syncUpstreamFirst = false;
|
||||
}
|
||||
debug('sync %s with query: %j, syncUpstreamFirst: %s', name, this.query, syncUpstreamFirst);
|
||||
if (type === 'package' && publish && !this.user.isAdmin) {
|
||||
this.status = 403;
|
||||
const error = '[no_perms] Only admin can publish';
|
||||
this.body = {
|
||||
error: 'no_perms',
|
||||
reason: 'Only admin can publish'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
@@ -46,10 +38,11 @@ exports.sync = function* () {
|
||||
type: type,
|
||||
publish: publish,
|
||||
noDep: noDep,
|
||||
syncUpstreamFirst: config.sourceNpmRegistryIsCNpm,
|
||||
syncUpstreamFirst: syncUpstreamFirst,
|
||||
syncFromBackupFile: syncFromBackupFile,
|
||||
};
|
||||
|
||||
var logId = yield* SyncModuleWorker.sync(name, username, options);
|
||||
var logId = yield SyncModuleWorker.sync(name, username, options);
|
||||
debug('sync %s got log id %j', name, logId);
|
||||
|
||||
this.status = 201;
|
||||
@@ -60,16 +53,27 @@ exports.sync = function* () {
|
||||
};
|
||||
|
||||
exports.getSyncLog = function* (next) {
|
||||
var logId = this.params.id || this.params[1];
|
||||
var logId = Number(this.params.id || this.params[1]);
|
||||
var offset = Number(this.query.offset) || 0;
|
||||
var row = yield* Log.get(logId);
|
||||
|
||||
if (!logId) { // NaN
|
||||
this.status = 404;
|
||||
return;
|
||||
}
|
||||
var row = yield Log.get(logId);
|
||||
if (!row) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
|
||||
var log = row.log.trim();
|
||||
var syncDone = row.log.indexOf('[done] Sync') >= 0;
|
||||
if (offset > 0) {
|
||||
log = log.split('\n').slice(offset).join('\n');
|
||||
if (!log && syncDone) {
|
||||
// append the last 1k string
|
||||
// the cnpm client sync need the `[done] Sync {name}` string to detect when sync task finished
|
||||
log = '... ignore long logs ...\n' + row.log.substring(row.log.length - 1024);
|
||||
}
|
||||
}
|
||||
this.body = {ok: true, log: log};
|
||||
this.body = { ok: true, syncDone: syncDone, log: log };
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,31 +1,39 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/total.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
const Total = require('../services/total');
|
||||
const version = require('../package.json').version;
|
||||
const config = require('../config');
|
||||
const getDownloadTotal = require('./utils').getDownloadTotal;
|
||||
const cacheClient = require('../common/cache');
|
||||
const logger = require('../common/logger');
|
||||
|
||||
var Total = require('../services/total');
|
||||
var version = require('../package.json').version;
|
||||
var config = require('../config');
|
||||
var getDownloadTotal = require('./utils').getDownloadTotal;
|
||||
|
||||
var startTime = '' + Date.now();
|
||||
const startTime = '' + Date.now();
|
||||
let cache = null;
|
||||
|
||||
module.exports = function* showTotal() {
|
||||
var r = yield [Total.get(), getDownloadTotal()];
|
||||
var total = r[0];
|
||||
var download = r[1];
|
||||
if (cache && Date.now() - cache.cache_time < 120000) {
|
||||
// cache 120 seconds
|
||||
this.body = cache;
|
||||
return;
|
||||
}
|
||||
|
||||
const cacheKey = 'registry_total';
|
||||
if (cacheClient) {
|
||||
const result = yield cacheClient.get(cacheKey);
|
||||
if (result) {
|
||||
this.body = JSON.parse(result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (cache) {
|
||||
// set cache_time fisrt, avoid query in next time
|
||||
cache.cache_time = Date.now();
|
||||
}
|
||||
|
||||
const r = yield [ Total.get(), getDownloadTotal() ];
|
||||
const total = r[0];
|
||||
const download = r[1];
|
||||
|
||||
total.download = download;
|
||||
total.db_name = 'registry';
|
||||
@@ -35,5 +43,18 @@ module.exports = function* showTotal() {
|
||||
total.donate = 'https://www.gittip.com/fengmk2';
|
||||
total.sync_model = config.syncModel;
|
||||
|
||||
cache = total;
|
||||
cache.cache_time = Date.now();
|
||||
|
||||
this.body = total;
|
||||
if (cacheClient) {
|
||||
cacheClient.pipeline()
|
||||
.set(cacheKey, JSON.stringify(total))
|
||||
// cache 12h
|
||||
.expire(cacheKey, 3600 * 12)
|
||||
.exec()
|
||||
.catch(err => {
|
||||
logger.error(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,25 +1,12 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/utils.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:utils');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var utility = require('utility');
|
||||
var ms = require('humanize-ms');
|
||||
var moment = require('moment');
|
||||
var rimraf = require('rimraf');
|
||||
var downloadTotalService = require('../services/download_total');
|
||||
var nfs = require('../common/nfs');
|
||||
var config = require('../config');
|
||||
@@ -34,9 +21,13 @@ exports.downloadAsReadStream = function* (key) {
|
||||
|
||||
var tmpPath = path.join(config.uploadDir,
|
||||
utility.randomString() + key.replace(/\//g, '-'));
|
||||
var tarball;
|
||||
function cleanup() {
|
||||
debug('cleanup %s', tmpPath);
|
||||
fs.unlink(tmpPath, utility.noop);
|
||||
rimraf(tmpPath, utility.noop);
|
||||
if (tarball) {
|
||||
tarball.destroy();
|
||||
}
|
||||
}
|
||||
debug('downloadAsReadStream() %s to %s', key, tmpPath);
|
||||
try {
|
||||
@@ -46,7 +37,7 @@ exports.downloadAsReadStream = function* (key) {
|
||||
cleanup();
|
||||
throw err;
|
||||
}
|
||||
var tarball = fs.createReadStream(tmpPath);
|
||||
tarball = fs.createReadStream(tmpPath);
|
||||
tarball.once('error', cleanup);
|
||||
tarball.once('end', cleanup);
|
||||
return tarball;
|
||||
@@ -56,11 +47,11 @@ exports.getDownloadTotal = function* (name) {
|
||||
var end = moment();
|
||||
var start = end.clone().subtract(1, 'months').startOf('month');
|
||||
var lastday = end.clone().subtract(1, 'days').format('YYYY-MM-DD');
|
||||
var lastweekStart = end.clone().subtract(1, 'weeks').startOf('week');
|
||||
var lastweekEnd = lastweekStart.clone().endOf('week').format('YYYY-MM-DD');
|
||||
var lastweekStart = end.clone().subtract(1, 'weeks').startOf('isoweek');
|
||||
var lastweekEnd = lastweekStart.clone().endOf('isoweek').format('YYYY-MM-DD');
|
||||
var lastmonthEnd = start.clone().endOf('month').format('YYYY-MM-DD');
|
||||
var thismonthStart = end.clone().startOf('month').format('YYYY-MM-DD');
|
||||
var thisweekStart = end.clone().startOf('week').format('YYYY-MM-DD');
|
||||
var thisweekStart = end.clone().startOf('isoweek').format('YYYY-MM-DD');
|
||||
start = start.format('YYYY-MM-DD');
|
||||
end = end.format('YYYY-MM-DD');
|
||||
lastweekStart = lastweekStart.format('YYYY-MM-DD');
|
||||
@@ -70,7 +61,7 @@ exports.getDownloadTotal = function* (name) {
|
||||
args.unshift(name);
|
||||
}
|
||||
|
||||
var rows = yield* downloadTotalService[method].apply(downloadTotalService, args);
|
||||
var rows = yield downloadTotalService[method].apply(downloadTotalService, args);
|
||||
var download = {
|
||||
today: 0,
|
||||
thisweek: 0,
|
||||
@@ -78,6 +69,7 @@ exports.getDownloadTotal = function* (name) {
|
||||
lastday: 0,
|
||||
lastweek: 0,
|
||||
lastmonth: 0,
|
||||
total: 0,
|
||||
};
|
||||
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
@@ -102,6 +94,9 @@ exports.getDownloadTotal = function* (name) {
|
||||
download.lastmonth += r.count;
|
||||
}
|
||||
}
|
||||
if (name) {
|
||||
download.total = yield downloadTotalService.getTotalByName();
|
||||
}
|
||||
return download;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,31 +1,20 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/web/badge.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utility = require('utility');
|
||||
var util = require('util');
|
||||
var config = require('../../config');
|
||||
var packageService = require('../../services/package');
|
||||
var DownloadTotal = require('../../services/download_total');
|
||||
|
||||
exports.version = function* () {
|
||||
var color = 'lightgrey';
|
||||
var version = 'invalid';
|
||||
var color = 'grey';
|
||||
var name = this.params[0];
|
||||
var tag = this.query.tag || 'latest';
|
||||
var info = yield* packageService.getModuleByTag(name, tag);
|
||||
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)) {
|
||||
@@ -40,23 +29,23 @@ exports.version = function* () {
|
||||
}
|
||||
}
|
||||
|
||||
var subject = config.badgeSubject.replace(/\-/g, '--');
|
||||
var subject = config.badgeSubject;
|
||||
if (this.query.subject) {
|
||||
subject = this.query.subject.replace(/\-/g, '--');
|
||||
subject = this.query.subject;
|
||||
}
|
||||
if (!version) {
|
||||
version = 'invalid';
|
||||
}
|
||||
version = version.replace(/\-/g, '--');
|
||||
var style = this.query.style || 'flat-square';
|
||||
var url = util.format('https://img.shields.io/badge/%s-%s-%s.svg?style=%s',
|
||||
utility.encodeURIComponent(subject), version, color, utility.encodeURIComponent(style));
|
||||
var url = config.badgeService.url(subject, version, { color, style });
|
||||
this.redirect(url);
|
||||
};
|
||||
|
||||
exports.downloads = function* () {
|
||||
// https://img.shields.io/badge/downloads-100k/month-brightgreen.svg?style=flat-square
|
||||
// https://dn-img-shields-io.qbox.me/badge/downloads-100k/month-brightgreen.svg?style=flat-square
|
||||
var name = this.params[0];
|
||||
var count = yield DownloadTotal.getTotalByName(name);
|
||||
var style = this.query.style || 'flat-square';
|
||||
var url = util.format('https://img.shields.io/badge/downloads-%s-brightgreen.svg?style=%s',
|
||||
count, utility.encodeURIComponent(style));
|
||||
var style = this.query.style;
|
||||
var url = config.badgeService.url('downloads', count, { style });
|
||||
this.redirect(url);
|
||||
};
|
||||
|
||||
@@ -1,20 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/web/package/list_privates.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var packageService = require('../../../services/package');
|
||||
var config = require('../../../config');
|
||||
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/web/package/search.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
* fengmk2 <m@fengmk2.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:web:package:search');
|
||||
var packageService = require('../../../services/package');
|
||||
var config = require('../../../config');
|
||||
|
||||
module.exports = function* search() {
|
||||
var params = this.params;
|
||||
var word = params.word || params[0];
|
||||
var limit = Number(this.query.limit) || 100;
|
||||
|
||||
if (limit > 10000) {
|
||||
limit = 10000;
|
||||
}
|
||||
|
||||
if (config.disableSearch) {
|
||||
return this.redirect(`/package/${encodeURIComponent(word)}`);
|
||||
}
|
||||
|
||||
debug('search %j', word);
|
||||
var result = yield* packageService.search(word);
|
||||
var result = yield packageService.search(word, {
|
||||
limit: limit
|
||||
});
|
||||
|
||||
var match = null;
|
||||
for (var i = 0; i < result.searchMatchs.length; i++) {
|
||||
|
||||
@@ -1,20 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/web/package/search_range.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var packageService = require('../../../services/package');
|
||||
|
||||
module.exports = function* searchRange() {
|
||||
@@ -26,7 +11,7 @@ module.exports = function* searchRange() {
|
||||
startKey = startKey.substring(0, startKey.length - 1);
|
||||
}
|
||||
var limit = Number(this.query.limit) || 20;
|
||||
var result = yield* packageService.search(startKey, {limit: limit});
|
||||
var result = yield packageService.search(startKey, {limit: limit});
|
||||
|
||||
var packages = result.searchMatchs.concat(result.keywordMatchs);
|
||||
|
||||
|
||||
@@ -1,20 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/web/package/show.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:web:package:show');
|
||||
var bytes = require('bytes');
|
||||
var giturl = require('giturl');
|
||||
@@ -27,6 +12,7 @@ var utils = require('../../utils');
|
||||
var setDownloadURL = require('../../../lib/common').setDownloadURL;
|
||||
var renderMarkdown = require('../../../common/markdown').render;
|
||||
var packageService = require('../../../services/package');
|
||||
var downloadTotalService = require('../../../services/download_total');
|
||||
|
||||
module.exports = function* show(next) {
|
||||
var params = this.params;
|
||||
@@ -49,9 +35,17 @@ module.exports = function* show(next) {
|
||||
}
|
||||
|
||||
var pkg = yield packageService[getPackageMethod].apply(packageService, getPackageArgs);
|
||||
if ((!pkg || !pkg.package) && tag) {
|
||||
// + if we can't find it by tag, it may be a non-semver version, check it then
|
||||
// + if we can't find it by version, it may be a tag, check it then
|
||||
getPackageMethod = version ? 'getModuleByTag' : 'getModule';
|
||||
pkg = yield packageService[getPackageMethod](name, tag);
|
||||
}
|
||||
|
||||
// if it's still not found
|
||||
if (!pkg || !pkg.package) {
|
||||
// check if unpublished
|
||||
var unpublishedInfo = yield* packageService.getUnpublishedModule(name);
|
||||
var unpublishedInfo = yield packageService.getUnpublishedModule(name);
|
||||
debug('show unpublished %j', unpublishedInfo);
|
||||
if (unpublishedInfo) {
|
||||
var data = {
|
||||
@@ -68,28 +62,61 @@ module.exports = function* show(next) {
|
||||
}
|
||||
}
|
||||
yield this.render('package_unpublished', {
|
||||
package: data
|
||||
package: data,
|
||||
title: 'Package - ' + name
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
|
||||
var r = yield [
|
||||
utils.getDownloadTotal(name),
|
||||
packageService.listDependents(name),
|
||||
packageService.listStarUserNames(name),
|
||||
packageService.listMaintainers(name)
|
||||
packageService.listMaintainers(name),
|
||||
packageService.listModulesByName(name),
|
||||
packageService.listModuleTags(name),
|
||||
downloadTotalService.getTotalByName(name),
|
||||
];
|
||||
var download = r[0];
|
||||
var dependents = r[1];
|
||||
var users = r[2];
|
||||
var maintainers = r[3];
|
||||
var rows = r[4];
|
||||
var tags = r[5];
|
||||
var downloadTotal = r[6];
|
||||
|
||||
const versionsMap = {};
|
||||
const versions = [];
|
||||
for (const row of rows) {
|
||||
var versionPkg = row.package;
|
||||
// pkg is string ... ignore it
|
||||
if (typeof versionPkg === 'string') {
|
||||
continue;
|
||||
}
|
||||
versionPkg.fromNow = moment(versionPkg.publish_time || row.publish_time).fromNow();
|
||||
versions.push(versionPkg);
|
||||
versionsMap[versionPkg.version] = versionPkg;
|
||||
}
|
||||
|
||||
for (const row of tags) {
|
||||
row.fromNow = versionsMap[row.version] && versionsMap[row.version].fromNow;
|
||||
}
|
||||
|
||||
pkg.package.fromNow = moment(pkg.publish_time).fromNow();
|
||||
pkg = pkg.package;
|
||||
pkg.users = users;
|
||||
pkg.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 {
|
||||
@@ -124,7 +151,9 @@ module.exports = function* show(next) {
|
||||
pkg.repository = null;
|
||||
}
|
||||
if (pkg.repository && pkg.repository.url) {
|
||||
pkg.repository.weburl = giturl.parse(pkg.repository.url) || pkg.repository.url;
|
||||
if (!pkg.repository.weburl) {
|
||||
pkg.repository.weburl = /^https?:\/\//.test(pkg.repository.url) ? pkg.repository.url : (giturl.parse(pkg.repository.url) || pkg.repository.url);
|
||||
}
|
||||
}
|
||||
if (!pkg.bugs) {
|
||||
pkg.bugs = {};
|
||||
@@ -148,6 +177,7 @@ module.exports = function* show(next) {
|
||||
}
|
||||
|
||||
pkg.registryUrl = '//' + config.registryHost + '/' + pkg.name;
|
||||
pkg.registryPackageUrl = '//' + config.registryHost + '/' + pkg.name + '/' + pkg.version;
|
||||
|
||||
// pkg.engines = {
|
||||
// "python": ">= 0.11.9",
|
||||
@@ -156,37 +186,58 @@ module.exports = function* show(next) {
|
||||
// "node2": ">= 0.10.9",
|
||||
// "node3": ">= 0.6.9",
|
||||
// };
|
||||
if (pkg.engines) {
|
||||
for (var k in pkg.engines) {
|
||||
var engine = String(pkg.engines[k] || '').trim();
|
||||
var color = 'blue';
|
||||
if (k.indexOf('node') === 0) {
|
||||
color = 'yellowgreen';
|
||||
var version = /(\d+\.\d+\.\d+)/.exec(engine);
|
||||
if (version) {
|
||||
version = version[0];
|
||||
if (/^0\.11\.\d+/.test(version)) {
|
||||
color = 'red';
|
||||
} else if (/^0\.10\./.test(version) ||
|
||||
/^0\.12\./.test(version) ||
|
||||
/^0\.14\./.test(version) ||
|
||||
/^[^0]+\./.test(version)) {
|
||||
color = 'brightgreen';
|
||||
}
|
||||
// "engines": "0.10.24",
|
||||
// invalid engines
|
||||
if (pkg.engines && typeof pkg.engines !== 'object') {
|
||||
pkg.engines = {};
|
||||
}
|
||||
|
||||
for (var k in pkg.engines) {
|
||||
var engine = String(pkg.engines[k] || '').trim();
|
||||
var color = 'blue';
|
||||
if (k.indexOf('node') === 0 || k.indexOf('install-') === 0) {
|
||||
color = 'green';
|
||||
var version = /(\d+\.)/.exec(engine);
|
||||
if (version) {
|
||||
version = version[0];
|
||||
if (/^[0123]\./.test(version)) {
|
||||
color = 'red';
|
||||
}
|
||||
}
|
||||
pkg.engines[k] = {
|
||||
version: engine,
|
||||
title: k + ': ' + engine,
|
||||
badgeURL: 'https://img.shields.io/badge/' + encodeURIComponent(k) +
|
||||
'-' + encodeURIComponent(engine) + '-' + color + '.svg?style=flat-square',
|
||||
};
|
||||
if (engine === '*') {
|
||||
color = 'red';
|
||||
}
|
||||
}
|
||||
pkg.engines[k] = {
|
||||
version: engine,
|
||||
title: k + ': ' + engine,
|
||||
badgeURL: config.badgeService.url(k, engine, { color }),
|
||||
};
|
||||
}
|
||||
|
||||
let packagephobiaSupport = downloadTotal >= config.packagephobiaMinDownloadCount;
|
||||
if (pkg._publish_on_cnpm) {
|
||||
pkg.isPrivate = true;
|
||||
// need download total >= 1000
|
||||
packagephobiaSupport = downloadTotal >= config.packagephobiaMinDownloadCount && config.packagephobiaSupportPrivatePackage;
|
||||
} else {
|
||||
pkg.isPrivate = false;
|
||||
// add security check badge
|
||||
pkg.snyk = {
|
||||
badge: `${config.snykUrl}/test/npm/${pkg.name}/badge.svg`,
|
||||
url: `${config.snykUrl}/test/npm/${pkg.name}`,
|
||||
};
|
||||
}
|
||||
if (packagephobiaSupport) {
|
||||
pkg.packagephobia = {
|
||||
badge: `${config.packagephobiaURL}/badge?p=${pkg.name}@${pkg.version}`,
|
||||
url: `${config.packagephobiaURL}/result?p=${pkg.name}@${pkg.version}`,
|
||||
};
|
||||
}
|
||||
|
||||
yield this.render('package', {
|
||||
title: 'Package - ' + pkg.name,
|
||||
package: pkg,
|
||||
download: download
|
||||
download: download,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,24 +1,10 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/web/user/show.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var config = require('../../../config');
|
||||
var packageService = require('../../../services/package');
|
||||
var userService = require('../../../services/user');
|
||||
var common = require('../../../lib/common');
|
||||
var he = require('he');
|
||||
|
||||
module.exports = function* showUser(next) {
|
||||
var name = this.params.name;
|
||||
@@ -29,15 +15,16 @@ module.exports = function* showUser(next) {
|
||||
var packages = r[0];
|
||||
var user = r[1];
|
||||
if (!user && !packages.length) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
|
||||
user = user || {};
|
||||
|
||||
var data = {
|
||||
name: name,
|
||||
email: user.email,
|
||||
json: user.json || {}
|
||||
email: user.email ? he.encode(user.email, { encodeEverything: true }) : user.email,
|
||||
json: user.json || {},
|
||||
isNpmUser: user.isNpmUser,
|
||||
};
|
||||
|
||||
if (data.json.login) {
|
||||
|
||||
13
dispatch.js
13
dispatch.js
@@ -1,18 +1,5 @@
|
||||
/**!
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com>
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var childProcess = require('child_process');
|
||||
var path = require('path');
|
||||
var util = require('util');
|
||||
|
||||
30
docker-compose.yml
Normal file
30
docker-compose.yml
Normal 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:
|
||||
205
docs/db.sql
205
docs/db.sql
@@ -3,81 +3,97 @@ CREATE TABLE IF NOT EXISTS `user` (
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(100) NOT NULL COMMENT 'user name',
|
||||
`salt` varchar(100) NOT NULL,
|
||||
`salt` varchar(100) NOT NULL COMMENT 'user salt',
|
||||
`password_sha` varchar(100) NOT NULL COMMENT 'user password hash',
|
||||
`ip` varchar(64) NOT NULL COMMENT 'user last request ip',
|
||||
`roles` varchar(200) NOT NULL DEFAULT '[]',
|
||||
`rev` varchar(40) NOT NULL,
|
||||
`email` varchar(400) NOT NULL,
|
||||
`json` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'json details',
|
||||
`roles` varchar(200) NOT NULL DEFAULT '[]' COMMENT 'user roles',
|
||||
`rev` varchar(40) NOT NULL COMMENT 'user rev',
|
||||
`email` varchar(400) NOT NULL COMMENT 'user email',
|
||||
`json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'json details',
|
||||
`npm_user` tinyint(1) DEFAULT '0' COMMENT 'user sync from npm or not, 1: true, other: false',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`name`),
|
||||
KEY `gmt_modified` (`gmt_modified`)
|
||||
UNIQUE KEY `uk_name` (`name`),
|
||||
KEY `idx_gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='user base info';
|
||||
-- ALTER TABLE `user`
|
||||
-- ADD `json` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'json details',
|
||||
-- ADD `npm_user` tinyint(1) DEFAULT '0' COMMENT 'user sync from npm or not, 1: true, other: false';
|
||||
-- ALTER TABLE `user` CHANGE `json` `json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'json details';
|
||||
-- ALTER TABLE `user`
|
||||
-- CHANGE `salt` `salt` varchar(100) NOT NULL COMMENT 'user salt',
|
||||
-- CHANGE `roles` `roles` varchar(200) NOT NULL DEFAULT '[]' COMMENT 'user roles',
|
||||
-- CHANGE `rev` `rev` varchar(40) NOT NULL COMMENT 'user rev',
|
||||
-- CHANGE `email` `email` varchar(400) NOT NULL COMMENT 'user email';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `module_keyword` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`keyword` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'keyword',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`description` longtext,
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`description` longtext COMMENT 'module description',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `keyword_module_name` (`keyword`,`name`),
|
||||
KEY `name` (`name`)
|
||||
UNIQUE KEY `uk_keyword_module_name` (`keyword`,`name`),
|
||||
KEY `idx_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module keyword';
|
||||
-- ALTER TABLE `module_keyword`
|
||||
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
-- CHANGE `description` `description` longtext COMMENT 'module description';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `module_star` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `user_module_name` (`user`,`name`),
|
||||
KEY `name` (`name`)
|
||||
UNIQUE KEY `uk_user_module_name` (`user`,`name`),
|
||||
KEY `idx_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module star';
|
||||
-- ALTER TABLE `module_star`
|
||||
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `module_maintainer` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `user_module_name` (`user`,`name`),
|
||||
KEY `name` (`name`)
|
||||
UNIQUE KEY `uk_user_module_name` (`user`,`name`),
|
||||
KEY `idx_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='private module maintainers';
|
||||
-- ALTER TABLE `module_maintainer`
|
||||
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `npm_module_maintainer` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `user_module_name` (`user`,`name`),
|
||||
KEY `name` (`name`)
|
||||
UNIQUE KEY `uk_user_module_name` (`user`,`name`),
|
||||
KEY `idx_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='npm original module maintainers';
|
||||
-- ALTER TABLE `npm_module_maintainer`
|
||||
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `module` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`author` varchar(100) NOT NULL,
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`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`);
|
||||
@@ -85,48 +101,97 @@ CREATE TABLE IF NOT EXISTS `module` (
|
||||
-- ALTER TABLE `module` CHANGE `description` `description` LONGTEXT CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||
-- show create table module\G
|
||||
-- ALTER TABLE `module` CHANGE `name` `name` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
|
||||
-- ALTER TABLE `module`
|
||||
-- CHANGE `author` `author` varchar(100) NOT NULL COMMENT 'module author',
|
||||
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
-- CHANGE `version` `version` varchar(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 IF NOT EXISTS `module_abbreviated` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`version` varchar(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`),
|
||||
UNIQUE KEY `uk_name` (`name`,`version`),
|
||||
KEY `idx_gmt_modified` (`gmt_modified`),
|
||||
KEY `idx_publish_time` (`publish_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module abbreviated info';
|
||||
-- ALTER TABLE `module_abbreviated`
|
||||
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
-- CHANGE `publish_time` `publish_time` bigint(20) unsigned COMMENT 'the publish time';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `package_readme` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`readme` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'the latest version readme',
|
||||
`version` varchar(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,
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`log` longtext,
|
||||
`username` varchar(100) NOT NULL COMMENT 'which username',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`log` longtext COMMENT 'the raw log',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `name` (`name`)
|
||||
KEY `idx_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module sync log';
|
||||
-- ALTER TABLE `module_log` CHANGE `name` `name` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
|
||||
-- ALTER TABLE `module_log`
|
||||
-- CHANGE `username` `username` varchar(100) NOT NULL COMMENT 'which username',
|
||||
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
-- CHANGE `log` `log` longtext COMMENT 'the raw log';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `tag` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`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`),
|
||||
KEY `gmt_modified` (`gmt_modified`)
|
||||
UNIQUE KEY `uk_name` (`name`, `tag`),
|
||||
KEY `idx_gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module tag';
|
||||
-- ALTER TABLE `tag` ADD `module_id` BIGINT( 20 ) UNSIGNED NOT NULL;
|
||||
-- ALTER TABLE `tag` CHANGE `name` `name` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
|
||||
-- ALTER TABLE `tag` ADD KEY `gmt_modified` (`gmt_modified`);
|
||||
-- ALTER TABLE `tag`
|
||||
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `module_unpublished` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`package` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'base info: tags, time, maintainers, description, versions',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`name`),
|
||||
KEY `gmt_modified` (`gmt_modified`)
|
||||
UNIQUE KEY `uk_name` (`name`),
|
||||
KEY `idx_gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module unpublished info';
|
||||
-- ALTER TABLE `module_unpublished`
|
||||
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `total` (
|
||||
`name` varchar(100) NOT NULL COMMENT 'total name',
|
||||
`name` varchar(214) NOT NULL COMMENT 'total name',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`module_delete` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'module delete count',
|
||||
`last_sync_time` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'last timestamp sync from official registry',
|
||||
@@ -150,6 +215,7 @@ INSERT INTO total(name, gmt_modified) VALUES('total', now())
|
||||
-- ALTER TABLE `total` ADD `fail_sync_num` int unsigned NOT NULL DEFAULT '0' COMMENT 'how many packages sync fail at this time'
|
||||
-- ALTER TABLE `total` ADD `left_sync_num` int unsigned NOT NULL DEFAULT '0' COMMENT 'how many packages left to be sync'
|
||||
-- ALTER TABLE `total` ADD `last_sync_module` varchar(100) NOT NULL COMMENT 'last sync success module name';
|
||||
-- ALTER TABLE `total` CHANGE `name` `name` varchar(214) NOT NULL COMMENT 'total name';
|
||||
|
||||
-- CREATE TABLE IF NOT EXISTS `download_total` (
|
||||
-- `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
@@ -168,7 +234,7 @@ 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',
|
||||
`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',
|
||||
@@ -202,43 +268,68 @@ CREATE TABLE IF NOT EXISTS `downloads` (
|
||||
`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`)
|
||||
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(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`deps` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'which module depend on this module',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`deps` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'which module depend on this module',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name_deps` (`name`,`deps`),
|
||||
KEY `name` (`name`)
|
||||
UNIQUE KEY `uk_name_deps` (`name`,`deps`),
|
||||
KEY `idx_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module deps';
|
||||
-- ALTER TABLE `module_deps`
|
||||
-- CHANGE `name` `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
-- CHANGE `deps` `deps` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'which module depend on this module';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dist_dir` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(200) NOT NULL COMMENT 'dir name',
|
||||
`parent` varchar(200) NOT NULL COMMENT 'parent dir' DEFAULT '/',
|
||||
`name` varchar(214) NOT NULL COMMENT 'dir name',
|
||||
`parent` varchar(214) NOT NULL COMMENT 'parent dir' DEFAULT '/',
|
||||
`date` varchar(20) COMMENT '02-May-2014 01:06',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`parent`, `name`),
|
||||
KEY `gmt_modified` (`gmt_modified`)
|
||||
UNIQUE KEY `uk_name` (`parent`, `name`),
|
||||
KEY `idx_gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='dist dir info';
|
||||
-- ALTER TABLE `dist_dir`
|
||||
-- CHANGE `name` `name` varchar(214) NOT NULL COMMENT 'dir name',
|
||||
-- CHANGE `parent` `parent` varchar(214) NOT NULL COMMENT 'parent dir' DEFAULT '/';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dist_file` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(100) NOT NULL COMMENT 'file name',
|
||||
`parent` varchar(200) NOT NULL COMMENT 'parent dir' DEFAULT '/',
|
||||
`name` varchar(214) NOT NULL COMMENT 'file name',
|
||||
`parent` varchar(214) NOT NULL COMMENT 'parent dir' DEFAULT '/',
|
||||
`date` varchar(20) COMMENT '02-May-2014 01:06',
|
||||
`size` int(10) unsigned NOT NULL COMMENT 'file size' DEFAULT '0',
|
||||
`sha1` varchar(40) COMMENT 'sha1 hex value',
|
||||
`url` varchar(2048),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `fullname` (`parent`, `name`),
|
||||
KEY `gmt_modified` (`gmt_modified`)
|
||||
UNIQUE KEY `uk_fullname` (`parent`, `name`),
|
||||
KEY `idx_gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='dist file info';
|
||||
-- ALTER TABLE `dist_file`
|
||||
-- CHANGE `name` `name` varchar(214) NOT NULL COMMENT 'file name',
|
||||
-- CHANGE `parent` `parent` varchar(214) NOT NULL COMMENT 'parent dir' DEFAULT '/';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `token` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`token` varchar(100) NOT NULL COMMENT 'token',
|
||||
`user_id` varchar(100) NOT NULL COMMENT 'user name',
|
||||
`readonly` tinyint NOT NULL DEFAULT 0 COMMENT 'readonly or not, 1: true, other: false',
|
||||
`token_key` varchar(200) NOT NULL COMMENT 'token sha512 hash',
|
||||
`cidr_whitelist` varchar(500) NOT NULL COMMENT 'ip list, ["127.0.0.1"]',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_token` (`token`),
|
||||
KEY `idx_user` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='token info';
|
||||
|
||||
262
docs/dockerize/config.js
Normal file
262
docs/dockerize/config.js
Normal file
@@ -0,0 +1,262 @@
|
||||
'use strict';
|
||||
|
||||
var mkdirp = require('mkdirp');
|
||||
var copy = require('copy-to');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var os = require('os');
|
||||
|
||||
var version = require('../package.json').version;
|
||||
var Nfs = require('fs-cnpm');
|
||||
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: new Nfs({
|
||||
dir: path.join(dataDir, 'nfs')
|
||||
}),
|
||||
// if set true, will 302 redirect to `nfs.url(dist.key)`
|
||||
downloadRedirectToNFS: false,
|
||||
|
||||
// registry url name
|
||||
registryHost: '127.0.0.1:7001',
|
||||
|
||||
/**
|
||||
* registry mode config
|
||||
*/
|
||||
|
||||
// enable private mode or not
|
||||
// private mode: only admins can publish, other users just can sync package from source npm
|
||||
// public mode: all users can publish
|
||||
enablePrivate: false,
|
||||
|
||||
// registry scopes, if don't set, means do not support scopes
|
||||
scopes: [ '@cnpm', '@cnpmtest', '@cnpm-test' ],
|
||||
|
||||
// some registry already have some private packages in global scope
|
||||
// but we want to treat them as scoped private packages,
|
||||
// so you can use this white list.
|
||||
privatePackages: [],
|
||||
|
||||
/**
|
||||
* sync configs
|
||||
*/
|
||||
|
||||
// the official npm registry
|
||||
// cnpm wont directly sync from this one
|
||||
// but sometimes will request it for some package infomations
|
||||
// please don't change it if not necessary
|
||||
officialNpmRegistry: 'https://registry.npmjs.com',
|
||||
officialNpmReplicate: 'https://replicate.npmjs.com',
|
||||
|
||||
// sync source, upstream registry
|
||||
// If you want to directly sync from official npm's registry
|
||||
// please drop them an email first
|
||||
sourceNpmRegistry: 'https://registry.npm.taobao.org',
|
||||
|
||||
// upstream registry is base on cnpm/cnpmjs.org or not
|
||||
// if your upstream is official npm registry, please turn it off
|
||||
sourceNpmRegistryIsCNpm: true,
|
||||
|
||||
// if install return 404, try to sync from source registry
|
||||
syncByInstall: true,
|
||||
|
||||
// sync mode select
|
||||
// none: do not sync any module, proxy all public modules from sourceNpmRegistry
|
||||
// exist: only sync exist modules
|
||||
// all: sync all modules
|
||||
syncModel: 'none', // 'none', 'all', 'exist'
|
||||
|
||||
syncConcurrency: 1,
|
||||
// sync interval, default is 10 minutes
|
||||
syncInterval: '10m',
|
||||
|
||||
// sync polular modules, default to false
|
||||
// because cnpm can't auto sync tag change for now
|
||||
// so we want to sync popular modules to ensure their tags
|
||||
syncPopular: false,
|
||||
syncPopularInterval: '1h',
|
||||
// top 100
|
||||
topPopular: 100,
|
||||
|
||||
// sync devDependencies or not, default is false
|
||||
syncDevDependencies: false,
|
||||
|
||||
// changes streaming sync
|
||||
syncChangesStream: false,
|
||||
handleSyncRegistry: 'http://127.0.0.1:7001',
|
||||
|
||||
// badge subject on http://shields.io/
|
||||
badgePrefixURL: 'https://img.shields.io/badge',
|
||||
badgeSubject: 'cnpm',
|
||||
|
||||
// custom user service, @see https://github.com/cnpm/cnpmjs.org/wiki/Use-Your-Own-User-Authorization
|
||||
// when you not intend to ingegrate with your company's user system, then use null, it would
|
||||
// use the default cnpm user system
|
||||
userService: null,
|
||||
|
||||
// always-auth https://docs.npmjs.com/misc/config#always-auth
|
||||
// Force npm to always require authentication when accessing the registry, even for GET requests.
|
||||
alwaysAuth: false,
|
||||
|
||||
// if you're behind firewall, need to request through http proxy, please set this
|
||||
// e.g.: `httpProxy: 'http://proxy.mycompany.com:8080'`
|
||||
httpProxy: null,
|
||||
|
||||
// snyk.io root url
|
||||
snykUrl: 'https://snyk.io',
|
||||
|
||||
// https://github.com/cnpm/cnpmjs.org/issues/1149
|
||||
// if enable this option, must create module_abbreviated and package_readme table in database
|
||||
enableAbbreviatedMetadata: true,
|
||||
|
||||
// global hook function: function* (envelope) {}
|
||||
// envelope format please see https://github.com/npm/registry/blob/master/docs/hooks/hooks-payload.md#payload
|
||||
globalHook: null,
|
||||
accelerateHostMap: {},
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
var customConfig;
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
customConfig = path.join(root, 'config', 'config.js');
|
||||
} else {
|
||||
// 1. try to load `$dataDir/config.json` first, not exists then goto 2.
|
||||
// 2. load config/config.js, everything in config.js will cover the same key in index.js
|
||||
customConfig = path.join(dataDir, 'config.json');
|
||||
if (!fs.existsSync(customConfig)) {
|
||||
customConfig = path.join(root, 'config', 'config.js');
|
||||
}
|
||||
}
|
||||
if (fs.existsSync(customConfig)) {
|
||||
copy(require(customConfig)).override(config);
|
||||
}
|
||||
}
|
||||
|
||||
mkdirp.sync(config.logdir);
|
||||
mkdirp.sync(config.uploadDir);
|
||||
|
||||
module.exports = config;
|
||||
|
||||
config.loadConfig = function (customConfig) {
|
||||
if (!customConfig) {
|
||||
return;
|
||||
}
|
||||
copy(customConfig).override(config);
|
||||
};
|
||||
BIN
docs/network.png
Normal file
BIN
docs/network.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 123 KiB |
98
docs/network.puml
Normal file
98
docs/network.puml
Normal 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
|
||||
@@ -9,6 +9,8 @@
|
||||
* [User](/docs/registry-api.md#user)
|
||||
* [Search](/docs/registry-api.md#search)
|
||||
|
||||
[](https://app.getpostman.com/run-collection/f6c8cb46358039bcd689#?env%5BRegistry%5D=W3sia2V5IjoicmVnaXN0cnkiLCJ0eXBlIjoidGV4dCIsInZhbHVlIjoiaHR0cHM6Ly9yZWdpc3RyeS5ucG0udGFvYmFvLm9yZyIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoicGFja2FnZSIsInZhbHVlIjoiY25wbSIsInR5cGUiOiJ0ZXh0IiwiZW5hYmxlZCI6dHJ1ZX1d)
|
||||
|
||||
## Schema
|
||||
|
||||
All API access is over HTTPS or HTTP,
|
||||
@@ -48,7 +50,7 @@ Status: 4xx
|
||||
|
||||
## Authentication
|
||||
|
||||
There is only one way to authenticate through the API.
|
||||
There are two ways to authenticate through the API.
|
||||
|
||||
## Basic Authentication
|
||||
|
||||
@@ -56,6 +58,12 @@ There is only one way to authenticate through the API.
|
||||
$ curl -u "username:password" https://registry.npmjs.org
|
||||
```
|
||||
|
||||
## Bearer Authentication
|
||||
|
||||
```bash
|
||||
$ curl -H "Authorization: Bearer ${UUId}" https://registry.npmjs.org
|
||||
```
|
||||
|
||||
## Failed login limit
|
||||
|
||||
```bash
|
||||
@@ -901,7 +909,8 @@ Status: 201 Created
|
||||
{
|
||||
"ok": true,
|
||||
"id": "org.couchdb.user:fengmk2",
|
||||
"rev": "32-984ee97e01aea166dcab6d1517c730e3"
|
||||
"rev": "32-984ee97e01aea166dcab6d1517c730e3",
|
||||
"token": "85d32fad-bd43-4dd7-9451-4f7d907313a2"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -954,3 +963,76 @@ Status: 201 Created
|
||||
```
|
||||
|
||||
## Search
|
||||
|
||||
## Token
|
||||
|
||||
- [Create token](/docs/registry-api.md#create-token)
|
||||
- [List token](/docs/registry-api.md#list-token)
|
||||
- [Delete token](/docs/registry-api.md#delete-token)
|
||||
|
||||
### Create token
|
||||
|
||||
* Authentication required.
|
||||
|
||||
```
|
||||
POST /-/npm/v1/tokens
|
||||
```
|
||||
|
||||
#### Input
|
||||
```json
|
||||
{
|
||||
"password": "123",
|
||||
"readonly": false,
|
||||
"cidr_whitelist": [
|
||||
"127.0.0.1"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Response 200
|
||||
```json
|
||||
HTTP/1.1 200 OK
|
||||
|
||||
{
|
||||
"token": "85d32fad-bd43-4dd7-9451-4f7d907313a2",
|
||||
"key": "d06309a210570ef71cd9c7bd4849e7e96eeaa841976e63326436f6fd320dc4bbd452710e4e0fedc2efc2ea4a793b7159e95e9596e85e00dee26adc3f8afbb97f",
|
||||
"cidr_whitelist": [ "127.0.0.1" ],
|
||||
"created": "2015-01-04T08:28:51.378Z",
|
||||
"updated": "2015-01-04T08:28:51.378Z",
|
||||
"readonly": false
|
||||
}
|
||||
```
|
||||
|
||||
### List token
|
||||
* Authentication required.
|
||||
|
||||
```
|
||||
GET /-/npm/v1/tokens
|
||||
```
|
||||
|
||||
### Input
|
||||
perPage=10&page=0
|
||||
|
||||
#### Response 200
|
||||
```json
|
||||
{
|
||||
"objects": [{
|
||||
"token": "85d32f...7313a2",
|
||||
"key": "d06309a210570ef71cd9c7bd4849e7e96eeaa841976e63326436f6fd320dc4bbd452710e4e0fedc2efc2ea4a793b7159e95e9596e85e00dee26adc3f8afbb97f",
|
||||
"cidr_whitelist": [ "127.0.0.1" ],
|
||||
"created": "2015-01-04T08:28:51.378Z",
|
||||
"updated": "2015-01-04T08:28:51.378Z",
|
||||
"readonly": false
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Delete token
|
||||
|
||||
* Authentication required.
|
||||
|
||||
```
|
||||
GET /-/npm/v1/tokens/token/:UUID
|
||||
```
|
||||
|
||||
#### Response 204
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
# Install & Get Started
|
||||
|
||||
## Deps
|
||||
|
||||
* MySQL Server: http://db4free.net/
|
||||
* qiniu CDN: http://www.qiniu.com/
|
||||
* redis session: https://garantiadata.com Support 24MB free spaces.
|
||||
* node: >=0.10.21
|
||||
|
||||
## Clone
|
||||
|
||||
```bash
|
||||
$ git clone git://github.com/fengmk2/cnpmjs.org.git $HOME/cnpmjs.org
|
||||
$ cd $HOME/cnpmjs.org
|
||||
```
|
||||
|
||||
## Create your `config.js`
|
||||
|
||||
```bash
|
||||
$ vim config/config.js
|
||||
```
|
||||
|
||||
`config.js` content sample:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
debug: false,
|
||||
enableCluster: true, // enable cluster mode
|
||||
logdir: 'your application log dir',
|
||||
mysqlServers: [
|
||||
{
|
||||
host: 'your mysql host',
|
||||
port: 3306,
|
||||
user: 'yourname',
|
||||
password: 'your password'
|
||||
}
|
||||
],
|
||||
mysqlDatabase: 'cnpmjs',
|
||||
redis: {
|
||||
host: 'your redist host',
|
||||
port: 6379,
|
||||
},
|
||||
qn: {
|
||||
accessKey: "your qiniu appkey",
|
||||
secretKey: "your secret key",
|
||||
bucket: "foobucket",
|
||||
domain: "http://foobucket.u.qiniudn.com"
|
||||
},
|
||||
nfs: null, // you can set a nfs to replace qiniu cdn
|
||||
enablePrivate: true, // enable private mode, only admin can publish, other use just can sync package from source npm
|
||||
admins: {
|
||||
admin: 'admin@cnpmjs.org',
|
||||
},
|
||||
syncModel: 'exist', //`all` sync all packages, `exist` only update exist packages, `none` do nothing
|
||||
};
|
||||
```
|
||||
|
||||
## Create MySQL Database and Tables
|
||||
|
||||
```bash
|
||||
$ mysql -u yourname -p
|
||||
|
||||
mysql> use cnpmjs;
|
||||
mysql> source docs/db.sql
|
||||
```
|
||||
|
||||
## Use your own CDN
|
||||
If you wan to use your own CDN instead of qiniu. Just look at `common/qnfs.js` and implement the interface like it, then pass it by set `config.nfs`.
|
||||
|
||||
## npm install
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
```
|
||||
|
||||
## start
|
||||
|
||||
```bash
|
||||
$ ./bin/nodejsctl start
|
||||
|
||||
Starting cnpmjs.org ...
|
||||
Start nodejs success. PID=27175
|
||||
```
|
||||
|
||||
## open registry and web
|
||||
|
||||
```bash
|
||||
# registry
|
||||
$ open http://localhost:7001
|
||||
# web
|
||||
$ open http://localhost:7002
|
||||
```
|
||||
|
||||
## use cnpm cli with your own registry
|
||||
You do not need to write another command line tool with your own registry,
|
||||
just alias [cnpm](http://github.com/fengmk2/cnpm), then you can get a npm client for you own registry.
|
||||
|
||||
```
|
||||
# install cnpm first
|
||||
npm install -g cnpm
|
||||
|
||||
# then alias lnpm to cnpm, but change config to your own registry
|
||||
alias lnpm='cnpm --registry=http://localhost:7001\
|
||||
--registryweb=http://localhost:7002\
|
||||
--cache=$HOME/.npm/.cache/lnpm\
|
||||
--disturl=http://cnpmjs.org/dist\
|
||||
--userconfig=$HOME/.lnpmrc'
|
||||
|
||||
#or put this in .zshrc or .bashrc
|
||||
echo "#lnpm alias\nalias lnpm='cnpm --registry=http://localhost:7001\
|
||||
--registryweb=http://localhost:7002\
|
||||
--cache=$HOME/.npm/.cache/lnpm\
|
||||
--disturl=http://cnpmjs.org/dist\
|
||||
--userconfig=$HOME/.lnpmrc'" >> $HOME/.zshrc && source $HOME/.zshrc
|
||||
```
|
||||
@@ -4,11 +4,13 @@ So `cnpm` is meaning: **Company npm**.
|
||||
|
||||
## Registry
|
||||
|
||||
- Our public registry: [r.cnpmjs.org](//r.cnpmjs.org), syncing from [registry.npmjs.org](//registry.npmjs.org)
|
||||
- Our public registry: [r.cnpmjs.org](https://r.cnpmjs.org), syncing from [registry.npmjs.com](https://registry.npmjs.com)
|
||||
- [cnpmjs.org](/) version: <span id="app-version"></span>
|
||||
- [Node.js](https://nodejs.org) version: <span id="node-version"></span>
|
||||
- For developers behind the GFW, please visit [the Chinese mirror](https://npm.taobao.org). 中国用户请访问[国内镜像站点](https://npm.taobao.org/)。
|
||||
- For developers in China, please visit [the China mirror](https://npm.taobao.org). 中国用户请访问[国内镜像站点](https://npm.taobao.org)。
|
||||
- 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>
|
||||
@@ -28,12 +30,17 @@ So `cnpm` is meaning: **Company npm**.
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="sync" style="display:none;">
|
||||
<h3>Sync Status</h3>
|
||||
<p id="sync-model"></p>
|
||||
<p>Last sync time is <span id="last-sync-time"></span>. </p>
|
||||
<p class="syncing alert alert-info">The sync worker is working in the backend now. </p>
|
||||
<div class="ant-alert ant-alert-info syncing">
|
||||
<span class="anticon ant-alert-icon anticon-info-circle"></span>
|
||||
<span class="ant-alert-description">The sync worker is working in the backend now. </span>
|
||||
</div>
|
||||
<div class="ant-table">
|
||||
<table class="sync-status">
|
||||
<tbody>
|
||||
<tr>
|
||||
@@ -48,6 +55,7 @@ So `cnpm` is meaning: **Company npm**.
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/readme.js"></script>
|
||||
@@ -58,7 +66,7 @@ Default style is `flat-square`.
|
||||
|
||||
### Version
|
||||
|
||||
Badge URL: `http://cnpmjs.org/badge/v/cnpmjs.org.svg` 
|
||||
Badge URL: `https://cnpmjs.org/badge/v/cnpmjs.org.svg` 
|
||||
|
||||
* `<0.1.0 & >=0.0.0`: 
|
||||
* `<1.0.0 & >=0.1.0`: 
|
||||
@@ -66,34 +74,34 @@ Badge URL: `http://cnpmjs.org/badge/v/cnpmjs.org.svg` 
|
||||
Badge URL: `https://cnpmjs.org/badge/d/cnpmjs.org.svg` 
|
||||
|
||||
## Usage
|
||||
|
||||
use our npm client [cnpm](https://github.com/cnpm/cnpm)(More suitable with cnpmjs.org and gzip support), you can get our client through npm:
|
||||
|
||||
```bash
|
||||
$ npm install -g cnpm --registry=http://registry.npm.taobao.org
|
||||
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
|
||||
```
|
||||
|
||||
Or you can alias NPM to use it:
|
||||
|
||||
```bash
|
||||
alias cnpm="npm --registry=http://registry.npm.taobao.org \
|
||||
alias cnpm="npm --registry=https://registry.npm.taobao.org \
|
||||
--cache=$HOME/.npm/.cache/cnpm \
|
||||
--disturl=http://registry.npm.taobao.org/mirrors/node \
|
||||
--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.npm.taobao.org \
|
||||
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=https://registry.npm.taobao.org \
|
||||
--cache=$HOME/.npm/.cache/cnpm \
|
||||
--disturl=http://registry.npm.taobao.org/mirrors/node \
|
||||
--disturl=https://npm.taobao.org/mirrors/node \
|
||||
--userconfig=$HOME/.cnpmrc"' >> ~/.zshrc && source ~/.zshrc
|
||||
```
|
||||
|
||||
### install
|
||||
|
||||
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](//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]
|
||||
@@ -138,10 +146,11 @@ $ cnpm info cnpm
|
||||
|
||||
Release [History](/history).
|
||||
|
||||
## npm and cnpm relation
|
||||
## npmjs.org, cnpmjs.org and npm.taobao.org relation
|
||||
|
||||

|
||||

|
||||
|
||||
## Sponsors
|
||||
|
||||
- [](http://www.ucloud.cn?sem=sdk-CNPMJS)
|
||||
- [](http://click.aliyun.com/m/4288/) [](https://devops.aliyun.com/?channel=pd_cnpm_github) (2016.2 - now)
|
||||
- [](http://www.ucloud.cn?sem=sdk-CNPMJS) (2015.3 - 2016.3)
|
||||
|
||||
12
index.js
12
index.js
@@ -1,17 +1,5 @@
|
||||
/**!
|
||||
* 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;
|
||||
|
||||
@@ -1,28 +1,19 @@
|
||||
/**!
|
||||
* cnpmjs.org - lib/common.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var crypto = require('crypto');
|
||||
var path = require('path');
|
||||
var config = require('../config');
|
||||
var utility = require('utility');
|
||||
var util = require('util');
|
||||
var config = require('../config');
|
||||
var BASIC_PREFIX = /basic /i;
|
||||
var BEARER_PREFIX = /bearer /i;
|
||||
|
||||
exports.getTarballFilepath = function (filename) {
|
||||
exports.getTarballFilepath = function (packageName, packageVersion, filename) {
|
||||
// ensure download file path unique
|
||||
// TODO: not only .tgz, and also other extname
|
||||
var name = filename.replace(/\.tgz$/, '.' + crypto.randomBytes(16).toString('hex') + '.tgz');
|
||||
var name = filename.replace(/\.tgz$/, '.' + crypto.randomBytes(16).toString('hex'));
|
||||
// use filename string md5 instead, fix "ENAMETOOLONG: name too long" error
|
||||
name = packageName.replace(/\//g, '-').replace(/\@/g, '') + '-' + packageVersion.substring(0, 20) + '.' + utility.md5(name) + '.tgz';
|
||||
return path.join(config.uploadDir, name);
|
||||
};
|
||||
|
||||
@@ -35,12 +26,57 @@ exports.getCDNKey = function (name, filename) {
|
||||
return '/' + name + '/-/' + filename;
|
||||
};
|
||||
|
||||
exports.getUnpublishFileKey = function (name) {
|
||||
return `/${name}/sync/unpublish/unpublish-package.json`;
|
||||
};
|
||||
|
||||
exports.getPackageFileCDNKey = function (name, version) {
|
||||
return `/${name}/sync/packages/package-${version}.json`;
|
||||
};
|
||||
|
||||
exports.getDistTagCDNKey = function (name, tag) {
|
||||
return `/${name}/sync/tags/tag-${tag}.json`;
|
||||
};
|
||||
|
||||
exports.getSyncTagDir = function (name) {
|
||||
return `${name}/sync/tags/`;
|
||||
};
|
||||
|
||||
exports.getSyncPackageDir = function (name) {
|
||||
return `${name}/sync/packages/`;
|
||||
};
|
||||
|
||||
const TAG_NAME_REG = /^tag-(.+)\.json$/;
|
||||
exports.getTagNameFromFileName = function (fileName) {
|
||||
const res = fileName.match(TAG_NAME_REG);
|
||||
return res && res[1];
|
||||
};
|
||||
|
||||
exports.isBackupTagFile = function (fileName) {
|
||||
return TAG_NAME_REG.test(fileName);
|
||||
};
|
||||
|
||||
const PACKAGE_NAME_REG = /^package-(.+)\.json$/;
|
||||
exports.getVersionFromFileName = function (fileName) {
|
||||
const res = fileName.match(PACKAGE_NAME_REG);
|
||||
return res && res[1];
|
||||
};
|
||||
|
||||
exports.isBackupPkgFile = function (fileName) {
|
||||
return PACKAGE_NAME_REG.test(fileName);
|
||||
};
|
||||
|
||||
exports.setDownloadURL = function (pkg, ctx, host) {
|
||||
if (pkg.dist) {
|
||||
host = host || ctx.host;
|
||||
host = host || config.registryHost || ctx.host;
|
||||
var protocol = config.protocol || ctx.protocol;
|
||||
pkg.dist.tarball = util.format('%s://%s/%s/download/%s-%s.tgz',
|
||||
ctx.protocol,
|
||||
protocol,
|
||||
host, pkg.name, pkg.name, pkg.version);
|
||||
if (ctx.querystring) {
|
||||
var backupUrl = pkg.dist.tarball;
|
||||
pkg.dist.tarball += '?' + ctx.querystring + '&other_urls=' + encodeURIComponent(backupUrl);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -73,8 +109,26 @@ exports.isLocalModule = function (mods) {
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,22 +1,10 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/auth.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:middleware:auth');
|
||||
var UserService = require('../services/user');
|
||||
var TokenService = require('../services/token');
|
||||
var config = require('../config');
|
||||
var common = require('../lib/common');
|
||||
|
||||
/**
|
||||
* Parse the request authorization
|
||||
@@ -27,24 +15,23 @@ module.exports = function () {
|
||||
return function* auth(next) {
|
||||
this.user = {};
|
||||
|
||||
var authorization = (this.get('authorization') || '').split(' ')[1] || '';
|
||||
authorization = authorization.trim();
|
||||
var authorization = (this.get('authorization') || '').trim();
|
||||
debug('%s %s with %j', this.method, this.url, authorization);
|
||||
if (!authorization) {
|
||||
return yield* unauthorized.call(this, next);
|
||||
return yield unauthorized.call(this, next);
|
||||
}
|
||||
|
||||
authorization = new Buffer(authorization, 'base64').toString().split(':');
|
||||
if (authorization.length !== 2) {
|
||||
return yield* unauthorized.call(this, next);
|
||||
}
|
||||
|
||||
var username = authorization[0];
|
||||
var password = authorization[1];
|
||||
|
||||
var row;
|
||||
try {
|
||||
row = yield* UserService.auth(username, password);
|
||||
var authorizeType = common.getAuthorizeType(this);
|
||||
|
||||
if (authorizeType === common.AuthorizeType.BASIC) {
|
||||
row = yield basicAuth(authorization);
|
||||
} else if (authorizeType === common.AuthorizeType.BEARER) {
|
||||
row = yield bearerAuth(authorization, this.method, this.ip);
|
||||
} else {
|
||||
return yield unauthorized.call(this, next);
|
||||
}
|
||||
} catch (err) {
|
||||
// do not response error here
|
||||
// many request do not need login
|
||||
@@ -53,27 +40,52 @@ module.exports = function () {
|
||||
|
||||
if (!row) {
|
||||
debug('auth fail user: %j, headers: %j', row, this.header);
|
||||
return yield* unauthorized.call(this, next);
|
||||
return yield unauthorized.call(this, next);
|
||||
}
|
||||
|
||||
this.user.name = row.login;
|
||||
this.user.isAdmin = row.site_admin;
|
||||
this.user.scopes = row.scopes;
|
||||
debug('auth pass user: %j, headers: %j', this.user, this.header);
|
||||
yield* next;
|
||||
yield next;
|
||||
};
|
||||
};
|
||||
|
||||
function* basicAuth(authorization) {
|
||||
authorization = authorization.split(' ')[1];
|
||||
authorization = Buffer.from(authorization, 'base64').toString();
|
||||
|
||||
var pos = authorization.indexOf(':');
|
||||
if (pos === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var username = authorization.slice(0, pos);
|
||||
var password = authorization.slice(pos + 1);
|
||||
|
||||
return yield UserService.auth(username, password);
|
||||
}
|
||||
|
||||
function* bearerAuth(authorization, method, ip) {
|
||||
var token = authorization.split(' ')[1];
|
||||
var isReadOperation = method === 'HEAD' || method === 'GET';
|
||||
return yield TokenService.validateToken(token, {
|
||||
isReadOperation: isReadOperation,
|
||||
accessIp: ip,
|
||||
});
|
||||
}
|
||||
|
||||
function* unauthorized(next) {
|
||||
if (!config.alwaysAuth || this.method !== 'GET') {
|
||||
return yield* next;
|
||||
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: 'unauthorized',
|
||||
reason: 'login first'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
} else {
|
||||
this.body = 'login first';
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/block.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
module.exports = function () {
|
||||
return function* block(next) {
|
||||
var ua = String(this.get('user-agent')).toLowerCase();
|
||||
@@ -23,6 +9,6 @@ module.exports = function () {
|
||||
message: 'forbidden Ruby user-agent, ip: ' + this.ip
|
||||
};
|
||||
}
|
||||
yield* next;
|
||||
yield next;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/editable.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var packageService = require('../services/package');
|
||||
|
||||
// admin or module's maintainer can modified the module
|
||||
@@ -22,11 +8,11 @@ module.exports = function* editable(next) {
|
||||
var moduleName = this.params.name || this.params[0];
|
||||
if (username && moduleName) {
|
||||
if (this.user.isAdmin) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
var isMaintainer = yield* packageService.isMaintainer(moduleName, username);
|
||||
var isMaintainer = yield packageService.isMaintainer(moduleName, username);
|
||||
if (isMaintainer) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +21,9 @@ module.exports = function* editable(next) {
|
||||
if (username) {
|
||||
message = username + ' ' + message;
|
||||
}
|
||||
message = '[forbidden] ' + message;
|
||||
this.body = {
|
||||
error: 'forbidden user',
|
||||
reason: message
|
||||
error: message,
|
||||
reason: message,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,30 +1,17 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/exists_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');
|
||||
|
||||
module.exports = function* (next) {
|
||||
var name = this.params.name || this.params[0];
|
||||
var pkg = yield packageService.getLatestModule(name);
|
||||
if (pkg) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
this.status = 404;
|
||||
const error = '[not_found] document not found';
|
||||
this.body = {
|
||||
error: 'not_found',
|
||||
reason: 'document not found'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,27 +1,13 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/limit.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var config = require('../config');
|
||||
var limit = require('koa-limit');
|
||||
|
||||
var limitConfig = config.limit;
|
||||
|
||||
if (!limitConfig.enable) {
|
||||
module.exports = function *ignoreLimit(next) {
|
||||
yield *next;
|
||||
module.exports = function* ignoreLimit(next) {
|
||||
yield next;
|
||||
};
|
||||
} else {
|
||||
module.exports = limit(limitConfig);
|
||||
|
||||
@@ -1,42 +1,35 @@
|
||||
/*!
|
||||
* cnpmjs.org - middleware/login.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var http = require('http');
|
||||
|
||||
module.exports = function *login(next) {
|
||||
if (this.path === '/-/ping' && this.query.write !== 'true') {
|
||||
yield next;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.user.error) {
|
||||
var status = this.user.error.status;
|
||||
this.status = http.STATUS_CODES[status]
|
||||
? status
|
||||
: 500;
|
||||
|
||||
const error = `[${this.user.error.name}] ${this.user.error.message}`;
|
||||
this.body = {
|
||||
error: this.user.error.name,
|
||||
reason: this.user.error.message
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.user.name) {
|
||||
this.status = 401;
|
||||
const error = '[unauthorized] Login first';
|
||||
this.body = {
|
||||
error: 'unauthorized',
|
||||
reason: 'Login first'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
yield *next;
|
||||
yield next;
|
||||
};
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/opensearch.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var template = '<?xml version="1.0" encoding="UTF-8"?>\
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">\
|
||||
<ShortName>CNPM</ShortName>\
|
||||
@@ -22,11 +8,13 @@ var template = '<?xml version="1.0" encoding="UTF-8"?>\
|
||||
<Url method="get" type="text/html" template="http://${host}/browse/keyword/{searchTerms}"/>\
|
||||
</OpenSearchDescription>';
|
||||
|
||||
module.exports = function *opensearch(next) {
|
||||
var config = require('../config');
|
||||
|
||||
module.exports = function* opensearch(next) {
|
||||
if (this.path === '/opensearch.xml') {
|
||||
this.type = 'text/xml';
|
||||
this.charset = 'utf-8';
|
||||
this.body = template.replace('${host}', this.host);
|
||||
this.body = template.replace('${host}', config.opensearch.host || this.host);
|
||||
}
|
||||
yield *next;
|
||||
yield next;
|
||||
};
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/proxy_to_npm.js
|
||||
*
|
||||
* Copyright(c) Alibaba Group Holding Limited.
|
||||
*
|
||||
* Authors:
|
||||
* 苏千 <suqian.yf@alipay.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:middleware:proxy_to_npm');
|
||||
var config = require('../config');
|
||||
|
||||
@@ -20,40 +7,78 @@ module.exports = function (options) {
|
||||
var redirectUrl = config.sourceNpmRegistry;
|
||||
var proxyUrls = [
|
||||
// /:pkg, dont contains scoped package
|
||||
/^\/[\w\-\.]+$/,
|
||||
// /:pkg/:versionOrTag
|
||||
/^\/[\w\-\.]+(?:\/[\w\-\.]+)?$/,
|
||||
// /-/package/:pkg/dist-tags
|
||||
/^\/\-\/package\/[\w\-\.]+\/dist-tags/,
|
||||
];
|
||||
var scopedUrls = [
|
||||
// scoped package
|
||||
/^\/(@[\w\-\.]+)\/[\w\-\.]+(?:\/[\w\-\.]+)?$/,
|
||||
/^\/\-\/package\/(@[\w\-\.]+)\/[\w\-\.]+\/dist\-tags/,
|
||||
];
|
||||
if (options && options.isWeb) {
|
||||
redirectUrl = redirectUrl.replace('//registry.', '//');
|
||||
redirectUrl = config.sourceNpmWeb || redirectUrl.replace('//registry.', '//');
|
||||
proxyUrls = [
|
||||
// /package/:pkg
|
||||
/^\/package\/[\w\-\.]+$/,
|
||||
/^\/package\/[\w\-\.]+/,
|
||||
];
|
||||
scopedUrls = [
|
||||
// scoped package
|
||||
/^\/package\/(@[\w\-\.]+)\/[\w\-\.]+/,
|
||||
];
|
||||
}
|
||||
|
||||
return function* proxyToNpm(next) {
|
||||
if (config.syncModel !== 'none') {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
|
||||
// syncModel === none
|
||||
// only proxy read requests
|
||||
if (this.method !== 'GET' && this.method !== 'HEAD') {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
|
||||
var pathname = this.path;
|
||||
var match;
|
||||
for (var i = 0; i < proxyUrls.length; i++) {
|
||||
match = proxyUrls[i].test(pathname);
|
||||
if (match) {
|
||||
break;
|
||||
var pathname = decodeURIComponent(this.path);
|
||||
|
||||
var isScoped = false;
|
||||
var isPublichScoped = false;
|
||||
// check scoped name
|
||||
if (config.scopes && config.scopes.length > 0) {
|
||||
for (var i = 0; i < scopedUrls.length; i++) {
|
||||
const m = scopedUrls[i].exec(pathname);
|
||||
if (m) {
|
||||
isScoped = true;
|
||||
if (config.scopes.indexOf(m[1]) !== -1) {
|
||||
// internal scoped
|
||||
isPublichScoped = false;
|
||||
} else {
|
||||
isPublichScoped = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
return yield* next;
|
||||
|
||||
var isPublich = false;
|
||||
if (!isScoped) {
|
||||
for (var i = 0; i < proxyUrls.length; i++) {
|
||||
isPublich = proxyUrls[i].test(pathname);
|
||||
if (isPublich) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var url = redirectUrl + this.url;
|
||||
debug('proxy to %s', url);
|
||||
this.redirect(url);
|
||||
if (isPublich || isPublichScoped) {
|
||||
var url = redirectUrl + this.url;
|
||||
debug('proxy isPublich: %s, isPublichScoped: %s, package to %s',
|
||||
isPublich, isPublichScoped, url);
|
||||
this.redirect(url);
|
||||
return;
|
||||
}
|
||||
|
||||
yield next;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,56 +1,68 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/publishable.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var packageService = require('../services/package');
|
||||
|
||||
var util = require('util');
|
||||
var config = require('../config');
|
||||
var debug = require('debug')('cnpmjs.org:middlewares/publishable');
|
||||
|
||||
module.exports = function *publishable(next) {
|
||||
// private mode, only admin user can publish
|
||||
if (config.enablePrivate && !this.user.isAdmin) {
|
||||
// admins always can publish and unpublish
|
||||
if (this.user.isAdmin) {
|
||||
return yield next;
|
||||
}
|
||||
|
||||
// private mode, normal user can't publish and unpublish
|
||||
if (config.enablePrivate) {
|
||||
this.status = 403;
|
||||
const error = '[no_perms] Private mode enable, only admin can publish this module';
|
||||
this.body = {
|
||||
error: 'no_perms',
|
||||
reason: 'Private mode enable, only admin can publish this module'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
// public mode, all user have permission to publish `scoped package`
|
||||
// public mode, normal user have permission to publish `scoped package`
|
||||
// and only can publish with scopes in `ctx.user.scopes`, default is `config.scopes`
|
||||
|
||||
var name = this.params.name || this.params[0];
|
||||
|
||||
// check if is private package list in config
|
||||
if (config.privatePackages && config.privatePackages.indexOf(name) !== -1) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
|
||||
// scope
|
||||
// check the package is already exists and is maintainer
|
||||
var result = yield packageService.authMaintainer(name, this.user.name);
|
||||
if (result.maintainers && result.maintainers.length) {
|
||||
if (result.isMaintainer) {
|
||||
return yield next;
|
||||
}
|
||||
this.status = 403;
|
||||
const error = '[forbidden] ' + this.user.name + ' not authorized to modify ' + name +
|
||||
', please contact maintainers: ' + result.maintainers.join(', ');
|
||||
this.body = {
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
// scoped module
|
||||
if (name[0] === '@') {
|
||||
if (checkScope(name, this)) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// none-scope
|
||||
if (checkNoneScope(name, this)) {
|
||||
return yield* next;
|
||||
}
|
||||
assertNoneScope(name, this);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -67,9 +79,10 @@ function checkScope(name, ctx) {
|
||||
if (ctx.user.scopes.indexOf(scope) === -1) {
|
||||
debug('assert scope %s error', name);
|
||||
ctx.status = 400;
|
||||
const error = util.format('[invalid] scope %s not match legal scopes: %s', scope, ctx.user.scopes.join(', '));
|
||||
ctx.body = {
|
||||
error: 'invalid scope',
|
||||
reason: util.format('scope %s not match legal scopes: %s', scope, ctx.user.scopes.join(', '))
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return false;
|
||||
}
|
||||
@@ -81,23 +94,20 @@ function checkScope(name, ctx) {
|
||||
* check if user have permission to publish without scope
|
||||
*/
|
||||
|
||||
function checkNoneScope(name, ctx) {
|
||||
// admins unpublished everything
|
||||
if (ctx.user.isAdmin && ctx.method === 'DELETE') {
|
||||
return true;
|
||||
}
|
||||
|
||||
function assertNoneScope(name, ctx) {
|
||||
ctx.status = 403;
|
||||
if (ctx.user.scopes.length === 0) {
|
||||
const error = '[no_perms] can\'t publish non-scoped package, please set `config.scopes`';
|
||||
ctx.body = {
|
||||
error: 'no_perms',
|
||||
reason: 'can\'t publish non-scoped package, please set `config.scopes`'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
const error = '[no_perms] only allow publish with ' + ctx.user.scopes.join(', ') + ' scope(s)';
|
||||
ctx.body = {
|
||||
error: 'no_perms',
|
||||
reason: 'only allow publish with ' + ctx.user.scopes.join(', ') + ' scope(s)'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,21 +1,7 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/registry_not_found.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
module.exports = function* notFound(next) {
|
||||
yield* next;
|
||||
yield next;
|
||||
|
||||
if (this.status && this.status !== 404) {
|
||||
return;
|
||||
@@ -25,8 +11,9 @@ module.exports = function* notFound(next) {
|
||||
}
|
||||
|
||||
this.status = 404;
|
||||
const error = '[not_found] document not found';
|
||||
this.body = {
|
||||
error: 'not_found',
|
||||
reason: 'document not found'
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -23,7 +23,8 @@ var staticDir = path.join(path.dirname(__dirname), 'public');
|
||||
module.exports = middlewares.staticCache(staticDir, {
|
||||
buffer: config.debug ? false : true,
|
||||
maxAge: config.debug ? 0 : 60 * 60 * 24 * 7,
|
||||
alas: {
|
||||
alias: {
|
||||
'/favicon.ico': '/favicon.png'
|
||||
}
|
||||
},
|
||||
gzip: config.enableCompress,
|
||||
});
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/sync_by_install.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
/**
|
||||
* Copyright(c) cnpm and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
@@ -17,33 +15,36 @@
|
||||
var config = require('../config');
|
||||
|
||||
/**
|
||||
* this.allowSync - allow sync triggle by cnpm install
|
||||
* {Boolean} this.allowSync - allow sync triggle by cnpm install
|
||||
*/
|
||||
|
||||
module.exports = function* syncByInstall(next) {
|
||||
this.allowSync = false;
|
||||
if (!config.syncByInstall) {
|
||||
// only config.enablePrivate should enable sync on install
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
// request not by node, consider it request from web, dont sync
|
||||
// request not by node, consider it request from web, don't sync
|
||||
var ua = this.get('user-agent');
|
||||
if (!ua || ua.indexOf('node') < 0) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
|
||||
// if request with `/xxx?write=true`, meaning the read request using for write, dont sync
|
||||
// if request with `/xxx?write=true`, meaning the read request using for write, don't sync
|
||||
if (this.query.write) {
|
||||
return yield* next;
|
||||
return yield next;
|
||||
}
|
||||
|
||||
var name = this.params.name || this.params[0];
|
||||
|
||||
// scoped package dont sync
|
||||
// private scoped package don't sync
|
||||
if (name && name[0] === '@') {
|
||||
return yield* next;
|
||||
var scope = name.split('/')[0];
|
||||
if (config.scopes.indexOf(scope) >= 0) {
|
||||
return yield next;
|
||||
}
|
||||
}
|
||||
|
||||
this.allowSync = true;
|
||||
yield* next;
|
||||
yield next;
|
||||
};
|
||||
|
||||
15
middleware/unpublishable.js
Normal file
15
middleware/unpublishable.js
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function *unpublishable(next) {
|
||||
// only admin user can unpublish
|
||||
if (!this.user.isAdmin) {
|
||||
this.status = 403;
|
||||
const error = '[no_perms] Only administrators can unpublish module';
|
||||
this.body = {
|
||||
error,
|
||||
reason: error,
|
||||
};
|
||||
return;
|
||||
}
|
||||
yield next;
|
||||
};
|
||||
@@ -1,23 +1,9 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/web_not_found.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:middleware:web_not_found');
|
||||
|
||||
module.exports = function* notFound(next) {
|
||||
yield* next;
|
||||
yield next;
|
||||
|
||||
if (this.status && this.status !== 404) {
|
||||
return;
|
||||
@@ -46,7 +32,7 @@ module.exports = function* notFound(next) {
|
||||
}
|
||||
|
||||
this.status = 404;
|
||||
yield* this.render('404', {
|
||||
yield this.render('404', {
|
||||
title: title,
|
||||
name: name
|
||||
});
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - models/_module_maintainer_class_methods.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
/**
|
||||
* list all module names by user
|
||||
* @param {String} user
|
||||
@@ -134,7 +120,7 @@ exports.updateMaintainers = function* (name, users) {
|
||||
remove: []
|
||||
};
|
||||
}
|
||||
var exists = yield* this.listMaintainers(name);
|
||||
var exists = yield this.listMaintainers(name);
|
||||
|
||||
var addUsers = users.filter(function (username) {
|
||||
// add user which in `users` but do not in `exists`
|
||||
@@ -156,4 +142,3 @@ exports.updateMaintainers = function* (name, users) {
|
||||
remove: removeUsers
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
// `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
// `gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
// `gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
// `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
// `name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
// `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',
|
||||
@@ -52,14 +52,14 @@
|
||||
// `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`)
|
||||
// UNIQUE KEY `uk_name_date` (`name`, `date`),
|
||||
// KEY `idx_date` (`date`)
|
||||
// ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module download total info';
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
return sequelize.define('DownloadTotal', {
|
||||
name: {
|
||||
type: DataTypes.STRING(100),
|
||||
type: DataTypes.STRING(214),
|
||||
allowNull: false,
|
||||
comment: 'module name',
|
||||
},
|
||||
@@ -260,10 +260,10 @@ module.exports = function (sequelize, DataTypes) {
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['name', 'date']
|
||||
fields: ['name', 'date'],
|
||||
},
|
||||
{
|
||||
fields: ['date']
|
||||
fields: ['date'],
|
||||
}
|
||||
],
|
||||
classMethods: {
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
/**!
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var path = require('path');
|
||||
var config = require('../config');
|
||||
var sequelize = require('../common/sequelize');
|
||||
|
||||
function load(name) {
|
||||
return sequelize.import(path.join(__dirname, name));
|
||||
}
|
||||
|
||||
var _ModuleAbbreviated = config.enableAbbreviatedMetadata ? load('module_abbreviated') : null;
|
||||
var _PackageReadme = config.enableAbbreviatedMetadata ? load('package_readme') : null;
|
||||
|
||||
module.exports = {
|
||||
sequelize: sequelize,
|
||||
Module: load('module'),
|
||||
@@ -34,17 +26,38 @@ module.exports = {
|
||||
User: load('user'),
|
||||
Total: load('total'),
|
||||
DownloadTotal: load('download_total'),
|
||||
Token: load('token'),
|
||||
|
||||
query: function* (sql, args) {
|
||||
var options = { replacements: args };
|
||||
var data = yield this.sequelize.query(sql, options).spread();
|
||||
var data = yield this.sequelize.query(sql, options);
|
||||
if (/select /i.test(sql)) {
|
||||
return data[0];
|
||||
}
|
||||
return data[1];
|
||||
},
|
||||
queryOne: function* (sql, args) {
|
||||
var rows = yield* this.query(sql, args);
|
||||
var rows = yield this.query(sql, args);
|
||||
return rows && rows[0];
|
||||
}
|
||||
},
|
||||
|
||||
get ModuleAbbreviated() {
|
||||
if (!config.enableAbbreviatedMetadata) {
|
||||
return null;
|
||||
}
|
||||
if (!_ModuleAbbreviated) {
|
||||
_ModuleAbbreviated = load('module_abbreviated');
|
||||
}
|
||||
return _ModuleAbbreviated;
|
||||
},
|
||||
|
||||
get PackageReadme() {
|
||||
if (!config.enableAbbreviatedMetadata) {
|
||||
return null;
|
||||
}
|
||||
if (!_PackageReadme) {
|
||||
_PackageReadme = load('package_readme');
|
||||
}
|
||||
return _PackageReadme;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - models/init_script.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var config = require('../config');
|
||||
|
||||
config.database.logging = console.log;
|
||||
@@ -35,7 +21,10 @@ if (username) {
|
||||
|
||||
var models = require('./');
|
||||
|
||||
models.sequelize.sync({ force: force })
|
||||
models.sequelize.sync({
|
||||
force: force,
|
||||
logging: console.log,
|
||||
})
|
||||
.then(function () {
|
||||
models.Total.init(function (err) {
|
||||
if (err) {
|
||||
|
||||
@@ -1,41 +1,25 @@
|
||||
/**!
|
||||
* cnpmjs.org - models/module.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
/*
|
||||
CREATE TABLE IF NOT EXISTS `module` (
|
||||
`id` INTEGER NOT NULL auto_increment ,
|
||||
`author` VARCHAR(100) NOT NULL,
|
||||
`name` VARCHAR(100) NOT NULL,
|
||||
`version` VARCHAR(30) NOT NULL,
|
||||
`description` LONGTEXT,
|
||||
`package` LONGTEXT,
|
||||
`dist_shasum` VARCHAR(100),
|
||||
`dist_tarball` VARCHAR(2048),
|
||||
`dist_size` INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
`publish_time` BIGINT(20) UNSIGNED,
|
||||
`gmt_create` DATETIME NOT NULL,
|
||||
`gmt_modified` DATETIME NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
)
|
||||
COMMENT 'module info' ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_general_ci;
|
||||
|
||||
CREATE UNIQUE INDEX `module_name_version` ON `module` (`name`, `version`);
|
||||
CREATE INDEX `module_gmt_modified` ON `module` (`gmt_modified`);
|
||||
CREATE INDEX `module_publish_time` ON `module` (`publish_time`);
|
||||
CREATE INDEX `module_author` ON `module` (`author`);
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`author` varchar(100) NOT NULL COMMENT 'module author',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`version` varchar(30) NOT NULL COMMENT 'module version',
|
||||
`description` longtext COMMENT 'module description',
|
||||
`package` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'package.json',
|
||||
`dist_shasum` varchar(100) DEFAULT NULL COMMENT 'module dist SHASUM',
|
||||
`dist_tarball` varchar(2048) DEFAULT NULL COMMENT 'module dist tarball',
|
||||
`dist_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'module dist size',
|
||||
`publish_time` bigint(20) unsigned COMMENT 'module publish time',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_name` (`name`,`version`),
|
||||
KEY `idx_gmt_modified` (`gmt_modified`),
|
||||
KEY `idx_publish_time` (`publish_time`),
|
||||
KEY `idx_author` (`author`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module info';
|
||||
*/
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
@@ -46,7 +30,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||
comment: 'first maintainer name'
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING(100),
|
||||
type: DataTypes.STRING(214),
|
||||
allowNull: false,
|
||||
comment: 'module name'
|
||||
},
|
||||
@@ -57,6 +41,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.LONGTEXT,
|
||||
comment: 'module description',
|
||||
},
|
||||
package: {
|
||||
type: DataTypes.LONGTEXT,
|
||||
@@ -65,19 +50,23 @@ module.exports = function (sequelize, DataTypes) {
|
||||
dist_shasum: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: true,
|
||||
comment: 'module dist SHASUM',
|
||||
},
|
||||
dist_tarball: {
|
||||
type: DataTypes.STRING(2048),
|
||||
allowNull: true,
|
||||
comment: 'module dist tarball',
|
||||
},
|
||||
dist_size: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
comment: 'module dist size',
|
||||
},
|
||||
publish_time: {
|
||||
type: DataTypes.BIGINT(20),
|
||||
allowNull: true,
|
||||
comment: 'module publish time',
|
||||
}
|
||||
}, {
|
||||
tableName: 'module',
|
||||
@@ -85,16 +74,16 @@ module.exports = function (sequelize, DataTypes) {
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['name', 'version']
|
||||
fields: ['name', 'version'],
|
||||
},
|
||||
{
|
||||
fields: ['gmt_modified']
|
||||
fields: ['gmt_modified'],
|
||||
},
|
||||
{
|
||||
fields: ['publish_time']
|
||||
fields: ['publish_time'],
|
||||
},
|
||||
{
|
||||
fields: ['author']
|
||||
fields: ['author'],
|
||||
}
|
||||
],
|
||||
classMethods: {
|
||||
|
||||
63
models/module_abbreviated.js
Normal file
63
models/module_abbreviated.js
Normal file
@@ -0,0 +1,63 @@
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
CREATE TABLE IF NOT EXISTS `module_abbreviated` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`version` varchar(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`),
|
||||
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';
|
||||
*/
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
return sequelize.define('ModuleAbbreviated', {
|
||||
name: {
|
||||
type: DataTypes.STRING(214),
|
||||
allowNull: false,
|
||||
comment: 'module name'
|
||||
},
|
||||
version: {
|
||||
type: DataTypes.STRING(30),
|
||||
allowNull: false,
|
||||
comment: 'module version'
|
||||
},
|
||||
package: {
|
||||
type: DataTypes.LONGTEXT,
|
||||
comment: 'package.json',
|
||||
},
|
||||
publish_time: {
|
||||
type: DataTypes.BIGINT(20),
|
||||
allowNull: true,
|
||||
comment: 'the publish time',
|
||||
}
|
||||
}, {
|
||||
tableName: 'module_abbreviated',
|
||||
comment: 'module abbreviated info',
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['name', 'version'],
|
||||
},
|
||||
{
|
||||
fields: ['gmt_modified'],
|
||||
},
|
||||
{
|
||||
fields: ['publish_time'],
|
||||
},
|
||||
],
|
||||
classMethods: {
|
||||
findByNameAndVersion: function* (name, version) {
|
||||
return yield this.find({
|
||||
where: { name: name, version: version }
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -18,18 +18,18 @@
|
||||
CREATE TABLE IF NOT EXISTS `module_deps` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`deps` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '`name` is deped by `deps`',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`deps` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'which module depend on this module',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `module_deps_name_deps` (`name`,`deps`),
|
||||
KEY `deps` (`module_deps_deps`)
|
||||
UNIQUE KEY `uk_name_deps` (`name`,`deps`),
|
||||
KEY `idx_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module deps';
|
||||
*/
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
return sequelize.define('ModuleDependency', {
|
||||
name: {
|
||||
type: DataTypes.STRING(100),
|
||||
type: DataTypes.STRING(214),
|
||||
allowNull: false,
|
||||
comment: 'module name',
|
||||
},
|
||||
@@ -46,10 +46,10 @@ module.exports = function (sequelize, DataTypes) {
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['name', 'deps']
|
||||
fields: ['name', 'deps'],
|
||||
},
|
||||
{
|
||||
fields: ['deps']
|
||||
fields: ['deps'],
|
||||
}
|
||||
],
|
||||
classMethods: {
|
||||
|
||||
@@ -19,11 +19,11 @@ CREATE TABLE IF NOT EXISTS `module_keyword` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`keyword` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'keyword',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`description` longtext,
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`description` longtext COMMENT 'module description',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `keyword_module_name` (`keyword`,`name`),
|
||||
KEY `name` (`name`)
|
||||
UNIQUE KEY `uk_keyword_module_name` (`keyword`,`name`),
|
||||
KEY `idx_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module keyword';
|
||||
*/
|
||||
|
||||
@@ -32,15 +32,17 @@ module.exports = function (sequelize, DataTypes) {
|
||||
keyword: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
comment: 'keyword',
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING(100),
|
||||
type: DataTypes.STRING(214),
|
||||
allowNull: false,
|
||||
comment: 'module name',
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.LONGTEXT,
|
||||
allowNull: true,
|
||||
comment: 'module description',
|
||||
}
|
||||
}, {
|
||||
tableName: 'module_keyword',
|
||||
@@ -49,10 +51,10 @@ module.exports = function (sequelize, DataTypes) {
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['keyword', 'name']
|
||||
fields: ['keyword', 'name'],
|
||||
},
|
||||
{
|
||||
fields: ['name']
|
||||
fields: ['name'],
|
||||
}
|
||||
],
|
||||
classMethods: {
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
CREATE TABLE IF NOT EXISTS `module_log` (
|
||||
CREATE TABLE IF NOT EXISTS `module_log` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`username` varchar(100) NOT NULL,
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`log` longtext,
|
||||
`username` varchar(100) NOT NULL COMMENT 'which username',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`log` longtext COMMENT 'the raw log',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `module_log_name` (`name`)
|
||||
KEY `idx_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module sync log';
|
||||
*/
|
||||
|
||||
@@ -35,19 +35,20 @@ module.exports = function (sequelize, DataTypes) {
|
||||
comment: 'user name'
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING(100),
|
||||
type: DataTypes.STRING(214),
|
||||
allowNull: false,
|
||||
comment: 'module name',
|
||||
},
|
||||
log: {
|
||||
type: DataTypes.LONGTEXT
|
||||
type: DataTypes.LONGTEXT,
|
||||
comment: 'the raw log',
|
||||
}
|
||||
}, {
|
||||
tableName: 'module_log',
|
||||
comment: 'module sync log',
|
||||
indexes: [
|
||||
{
|
||||
fields: ['name']
|
||||
fields: ['name'],
|
||||
}
|
||||
],
|
||||
classMethods: {
|
||||
|
||||
@@ -19,10 +19,10 @@ CREATE TABLE IF NOT EXISTS `module_maintainer` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `module_maintainer_user_name` (`user`,`name`),
|
||||
KEY `name` (`name`)
|
||||
UNIQUE KEY `uk_user_module_name` (`user`,`name`),
|
||||
KEY `idx_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='private module maintainers';
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||
comment: 'user name'
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING(100),
|
||||
type: DataTypes.STRING(214),
|
||||
allowNull: false,
|
||||
comment: 'module name',
|
||||
}
|
||||
@@ -45,10 +45,10 @@ module.exports = function (sequelize, DataTypes) {
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['user', 'name']
|
||||
fields: ['user', 'name'],
|
||||
},
|
||||
{
|
||||
fields: ['name']
|
||||
fields: ['name'],
|
||||
}
|
||||
],
|
||||
classMethods: require('./_module_maintainer_class_methods'),
|
||||
|
||||
@@ -19,10 +19,10 @@ CREATE TABLE IF NOT EXISTS `module_star` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `module_star_user_name` (`user`,`name`),
|
||||
KEY `module_star_name` (`name`)
|
||||
UNIQUE KEY `uk_user_module_name` (`user`,`name`),
|
||||
KEY `idx_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module star';
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||
comment: 'user name'
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING(100),
|
||||
type: DataTypes.STRING(214),
|
||||
allowNull: false,
|
||||
comment: 'module name',
|
||||
}
|
||||
@@ -45,10 +45,10 @@ module.exports = function (sequelize, DataTypes) {
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['user', 'name']
|
||||
fields: ['user', 'name'],
|
||||
},
|
||||
{
|
||||
fields: ['name']
|
||||
fields: ['name'],
|
||||
}
|
||||
],
|
||||
classMethods: {
|
||||
|
||||
@@ -21,18 +21,18 @@ CREATE TABLE IF NOT EXISTS `module_unpublished` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`package` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'base info: tags, time, maintainers, description, versions',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `module_unpublished_name` (`name`),
|
||||
KEY `module_unpublished_gmt_modified` (`gmt_modified`)
|
||||
UNIQUE KEY `uk_name` (`name`),
|
||||
KEY `idx_gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module unpublished info';
|
||||
*/
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
return sequelize.define('ModuleUnpublished', {
|
||||
name: {
|
||||
type: DataTypes.STRING(100),
|
||||
type: DataTypes.STRING(214),
|
||||
allowNull: false,
|
||||
comment: 'module name',
|
||||
},
|
||||
@@ -48,10 +48,10 @@ module.exports = function (sequelize, DataTypes) {
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['name']
|
||||
fields: ['name'],
|
||||
},
|
||||
{
|
||||
fields: ['gmt_modified']
|
||||
fields: ['gmt_modified'],
|
||||
}
|
||||
],
|
||||
classMethods: {
|
||||
@@ -70,7 +70,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||
});
|
||||
if (row) {
|
||||
row.package = pkg;
|
||||
if (row.isDirty) {
|
||||
if (row.changed()) {
|
||||
row = yield row.save(['package']);
|
||||
}
|
||||
return row;
|
||||
|
||||
@@ -19,10 +19,10 @@ CREATE TABLE IF NOT EXISTS `npm_module_maintainer` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `npm_module_maintainer_user_name` (`user`,`name`),
|
||||
KEY `name` (`name`)
|
||||
UNIQUE KEY `uk_user_module_name` (`user`,`name`),
|
||||
KEY `idx_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='npm original module maintainers';
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||
comment: 'user name'
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING(100),
|
||||
type: DataTypes.STRING(214),
|
||||
allowNull: false,
|
||||
comment: 'module name',
|
||||
}
|
||||
@@ -45,10 +45,10 @@ module.exports = function (sequelize, DataTypes) {
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['user', 'name']
|
||||
fields: ['user', 'name'],
|
||||
},
|
||||
{
|
||||
fields: ['name']
|
||||
fields: ['name'],
|
||||
}
|
||||
],
|
||||
classMethods: require('./_module_maintainer_class_methods'),
|
||||
|
||||
53
models/package_readme.js
Normal file
53
models/package_readme.js
Normal file
@@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
CREATE TABLE IF NOT EXISTS `package_readme` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(214) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`readme` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'the latest version readme',
|
||||
`version` varchar(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';
|
||||
*/
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
return sequelize.define('PackageReadme', {
|
||||
name: {
|
||||
type: DataTypes.STRING(214),
|
||||
allowNull: false,
|
||||
comment: 'module name'
|
||||
},
|
||||
version: {
|
||||
type: DataTypes.STRING(30),
|
||||
allowNull: false,
|
||||
comment: 'module latest version'
|
||||
},
|
||||
readme: {
|
||||
type: DataTypes.LONGTEXT,
|
||||
comment: 'latest version readme',
|
||||
},
|
||||
}, {
|
||||
tableName: 'package_readme',
|
||||
comment: 'package latest readme',
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['name'],
|
||||
},
|
||||
{
|
||||
fields: ['gmt_modified'],
|
||||
},
|
||||
],
|
||||
classMethods: {
|
||||
findByName: function* (name) {
|
||||
return yield this.find({
|
||||
where: { name: name },
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -19,20 +19,20 @@ CREATE TABLE IF NOT EXISTS `tag` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`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 `tag_name_tag` (`name`, `tag`),
|
||||
KEY `tag_gmt_modified` (`gmt_modified`)
|
||||
UNIQUE KEY `uk_name` (`name`, `tag`),
|
||||
KEY `idx_gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module tag';
|
||||
*/
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
return sequelize.define('Tag', {
|
||||
name: {
|
||||
type: DataTypes.STRING(100),
|
||||
type: DataTypes.STRING(214),
|
||||
allowNull: false,
|
||||
comment: 'module name',
|
||||
},
|
||||
@@ -57,10 +57,10 @@ module.exports = function (sequelize, DataTypes) {
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['name', 'tag']
|
||||
fields: ['name', 'tag'],
|
||||
},
|
||||
{
|
||||
fields: ['gmt_modified']
|
||||
fields: ['gmt_modified'],
|
||||
}
|
||||
],
|
||||
classMethods: {
|
||||
|
||||
117
models/token.js
Normal file
117
models/token.js
Normal file
@@ -0,0 +1,117 @@
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
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_id` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='token info';
|
||||
*/
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
return sequelize.define('Token', {
|
||||
token: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
comment: 'token',
|
||||
},
|
||||
userId: {
|
||||
field: 'user_id',
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
comment: 'user name'
|
||||
},
|
||||
readonly: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
comment: 'readonly or not, 1: true, other: false',
|
||||
},
|
||||
key: {
|
||||
field: 'token_key',
|
||||
type: DataTypes.STRING(256),
|
||||
allowNull: false,
|
||||
comment: 'token sha512 hash',
|
||||
},
|
||||
cidrWhitelist: {
|
||||
field: 'cidr_whitelist',
|
||||
type: DataTypes.STRING(500),
|
||||
allowNull: false,
|
||||
comment: 'ip list, ["127.0.0.1"]',
|
||||
get: function () {
|
||||
try {
|
||||
return JSON.parse(this.getDataValue('cidrWhitelist'));
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
set: function (val) {
|
||||
try {
|
||||
var stringifyVal = JSON.stringify(val);
|
||||
this.setDataValue('cidrWhitelist', stringifyVal);
|
||||
} catch (_) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
},
|
||||
}, {
|
||||
tableName: 'token',
|
||||
comment: 'token info',
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: [ 'token' ],
|
||||
},
|
||||
{
|
||||
fields: [ 'user_id' ],
|
||||
}
|
||||
],
|
||||
classMethods: {
|
||||
findByToken: function* (token) {
|
||||
return yield this.find({ where: { token: token } });
|
||||
},
|
||||
add: function* (tokenObj) {
|
||||
var row = this.build(tokenObj);
|
||||
return yield row.save();
|
||||
},
|
||||
listByUser: function* (userId, offset, limit) {
|
||||
return yield this.findAll({
|
||||
where: {
|
||||
userId: userId,
|
||||
},
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
order: 'id asc',
|
||||
});
|
||||
},
|
||||
deleteByKeyOrToken: function* (userId, keyOrToken) {
|
||||
const self = this;
|
||||
yield sequelize.transaction(function (t) {
|
||||
return self.destroy({
|
||||
where: {
|
||||
userId: userId,
|
||||
$or: [
|
||||
{ key: { like: keyOrToken + '%' } },
|
||||
{ token: keyOrToken },
|
||||
],
|
||||
},
|
||||
transaction: t,
|
||||
}).then(function (deleteRows) {
|
||||
// Key like query should not match more than 1 row
|
||||
if (deleteRows > 1) {
|
||||
throw new Error(`Token ID "${keyOrToken}" was ambiguous`);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
// CREATE TABLE IF NOT EXISTS `total` (
|
||||
// `name` varchar(100) NOT NULL COMMENT 'total name',
|
||||
// `name` varchar(214) NOT NULL COMMENT 'total name',
|
||||
// `gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
// `module_delete` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'module delete count',
|
||||
// `last_sync_time` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'last timestamp sync from official registry',
|
||||
@@ -35,7 +35,7 @@
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
return sequelize.define('Total', {
|
||||
name: {
|
||||
type: DataTypes.STRING(100),
|
||||
type: DataTypes.STRING(214),
|
||||
primaryKey: true,
|
||||
comment: 'total name'
|
||||
},
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - models/user.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utility = require('utility');
|
||||
var utils = require('./utils');
|
||||
|
||||
@@ -23,17 +9,17 @@ CREATE TABLE IF NOT EXISTS `user` (
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(100) NOT NULL COMMENT 'user name',
|
||||
`salt` varchar(100) NOT NULL,
|
||||
`salt` varchar(100) NOT NULL COMMENT 'user salt',
|
||||
`password_sha` varchar(100) NOT NULL COMMENT 'user password hash',
|
||||
`ip` varchar(64) NOT NULL COMMENT 'user last request ip',
|
||||
`roles` varchar(200) NOT NULL DEFAULT '[]',
|
||||
`rev` varchar(40) NOT NULL,
|
||||
`email` varchar(400) NOT NULL,
|
||||
`json` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'json details',
|
||||
`roles` varchar(200) NOT NULL DEFAULT '[]' COMMENT 'user roles',
|
||||
`rev` varchar(40) NOT NULL COMMENT 'user rev',
|
||||
`email` varchar(400) NOT NULL COMMENT 'user email',
|
||||
`json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'json details',
|
||||
`npm_user` tinyint(1) DEFAULT '0' COMMENT 'user sync from npm or not, 1: true, other: false',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `user_name` (`name`),
|
||||
KEY `user_gmt_modified` (`gmt_modified`)
|
||||
UNIQUE KEY `uk_name` (`name`),
|
||||
KEY `idx_gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='user base info';
|
||||
*/
|
||||
|
||||
@@ -47,6 +33,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||
salt: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
comment: 'user salt',
|
||||
},
|
||||
password_sha: {
|
||||
type: DataTypes.STRING(100),
|
||||
@@ -62,14 +49,17 @@ module.exports = function (sequelize, DataTypes) {
|
||||
type: DataTypes.STRING(200),
|
||||
allowNull: false,
|
||||
defaultValue: '[]',
|
||||
comment: 'user roles',
|
||||
},
|
||||
rev: {
|
||||
type: DataTypes.STRING(40),
|
||||
allowNull: false,
|
||||
comment: 'user rev',
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.STRING(400),
|
||||
allowNull: false,
|
||||
comment: 'user email',
|
||||
},
|
||||
json: {
|
||||
type: DataTypes.LONGTEXT,
|
||||
@@ -90,10 +80,10 @@ module.exports = function (sequelize, DataTypes) {
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['name']
|
||||
fields: ['name'],
|
||||
},
|
||||
{
|
||||
fields: ['gmt_modified']
|
||||
fields: ['gmt_modified'],
|
||||
}
|
||||
],
|
||||
classMethods: {
|
||||
@@ -104,7 +94,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||
|
||||
// read
|
||||
auth: function* (name, password) {
|
||||
var user = yield* this.findByName(name);
|
||||
var user = yield this.findByName(name);
|
||||
if (user) {
|
||||
var sha = this.createPasswordSha(password, user.salt);
|
||||
if (user.password_sha !== sha) {
|
||||
@@ -141,7 +131,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||
|
||||
// write
|
||||
saveNpmUser: function* (data) {
|
||||
var user = yield* this.findByName(data.name);
|
||||
var user = yield this.findByName(data.name);
|
||||
if (!user) {
|
||||
user = this.build({
|
||||
isNpmUser: true,
|
||||
@@ -155,14 +145,14 @@ module.exports = function (sequelize, DataTypes) {
|
||||
user.json = data;
|
||||
user.email = data.email || '';
|
||||
user.rev = data._rev || '';
|
||||
if (user.isDirty) {
|
||||
if (user.changed()) {
|
||||
user = yield user.save();
|
||||
}
|
||||
return user;
|
||||
},
|
||||
saveCustomUser: function* (data) {
|
||||
var name = data.user.login;
|
||||
var user = yield* this.findByName(name);
|
||||
var user = yield this.findByName(name);
|
||||
if (!user) {
|
||||
user = this.build({
|
||||
isNpmUser: false,
|
||||
@@ -182,7 +172,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||
user.rev = rev;
|
||||
user.salt = salt;
|
||||
user.password_sha = passwordSha;
|
||||
if (user.isDirty) {
|
||||
if (user.changed()) {
|
||||
user = yield user.save();
|
||||
}
|
||||
return user;
|
||||
@@ -230,7 +220,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||
roles = '[]';
|
||||
}
|
||||
|
||||
var row = yield* this.findByName(user.name);
|
||||
var row = yield this.findByName(user.name);
|
||||
if (!row) {
|
||||
return null;
|
||||
}
|
||||
|
||||
139
package.json
139
package.json
@@ -1,78 +1,97 @@
|
||||
{
|
||||
"name": "cnpmjs.org",
|
||||
"version": "2.2.1",
|
||||
"version": "3.0.0-rc.50",
|
||||
"description": "Private npm registry and web for Enterprise, base on MySQL and Simple Store Service",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "make install && make jshint && make test",
|
||||
"dev": "DEBUG=cnpm* node dispatch.js",
|
||||
"test": "make jshint && make test",
|
||||
"test-local": "make test",
|
||||
"start": "./bin/nodejsctl start && cp History.md docs/web/history.md",
|
||||
"status": "./bin/nodejsctl status",
|
||||
"stop": "./bin/nodejsctl stop"
|
||||
"stop": "./bin/nodejsctl stop",
|
||||
"ci": "make test-travis-mysql"
|
||||
},
|
||||
"bin": {
|
||||
"cnpmjs.org": "bin/cli.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"agentkeepalive": "~1.2.0",
|
||||
"bluebird": "~2.9.7",
|
||||
"bytes": "~1.0.0",
|
||||
"cfork": "~1.2.2",
|
||||
"co": "~4.3.1",
|
||||
"co-defer": "~1.0.0",
|
||||
"agentkeepalive": "^4.0.0",
|
||||
"await-event": "^2.1.0",
|
||||
"bytes": "^2.5.0",
|
||||
"cfork": "^1.6.1",
|
||||
"changes-stream": "^1.1.0",
|
||||
"co": "^4.6.0",
|
||||
"co-defer": "^1.0.0",
|
||||
"co-gather": "~0.0.1",
|
||||
"co-sleep": "~0.0.1",
|
||||
"commander": "~2.6.0",
|
||||
"copy-to": "~2.0.1",
|
||||
"debug": "~2.1.1",
|
||||
"error-formater": "~1.0.3",
|
||||
"fs-cnpm": "~1.2.0",
|
||||
"giturl": "~0.0.3",
|
||||
"gnode": "~0.1.1",
|
||||
"graceful": "~1.0.0",
|
||||
"gravatar": "~1.1.0",
|
||||
"humanize-ms": "~1.0.1",
|
||||
"commander": "^2.11.0",
|
||||
"copy-to": "^2.0.1",
|
||||
"debug": "^3.0.0",
|
||||
"error-formater": "^1.0.3",
|
||||
"fs-cnpm": "^2.1.0",
|
||||
"giturl": "^1.0.0",
|
||||
"graceful": "^1.0.1",
|
||||
"gravatar": "^1.6.0",
|
||||
"he": "^1.2.0",
|
||||
"humanize-ms": "^1.2.1",
|
||||
"humanize-number": "~0.0.2",
|
||||
"is-type-of": "~0.3.1",
|
||||
"kcors": "~1.0.1",
|
||||
"koa": "~0.17.0",
|
||||
"koa-limit": "~1.0.2",
|
||||
"koa-markdown": "~2.0.1",
|
||||
"koa-middlewares": "~2.1.0",
|
||||
"koa-mock": "~1.1.4",
|
||||
"koa-safe-jsonp": "~0.3.0",
|
||||
"markdown-it": "~3.0.6",
|
||||
"mime": "~1.3.4",
|
||||
"mini-logger": "~1.0.0",
|
||||
"mkdirp": "~0.5.0",
|
||||
"moment": "~2.9.0",
|
||||
"mysql": "~2.5.4",
|
||||
"nodemailer": "~1.3.0",
|
||||
"semver": "~4.2.0",
|
||||
"sequelize": "~2.0.1",
|
||||
"thunkify-wrap": "~1.0.4",
|
||||
"treekill": "~1.0.0",
|
||||
"tunnel-agent": "^0.4.0",
|
||||
"urllib": "~2.2.2",
|
||||
"utility": "~1.3.0",
|
||||
"xss": "~0.1.20"
|
||||
"ioredis": "^4.6.2",
|
||||
"ip-regex": "^4.1.0",
|
||||
"is-type-of": "^1.2.0",
|
||||
"kcors": "^1.2.1",
|
||||
"koa": "^1.2.0",
|
||||
"koa-bodyparser": "^2.5.0",
|
||||
"koa-conditional-get": "^1.0.4",
|
||||
"koa-etag": "^2.1.2",
|
||||
"koa-limit": "^1.0.2",
|
||||
"koa-markdown": "^2.0.2",
|
||||
"koa-maxrequests": "^1.0.0",
|
||||
"koa-middlewares": "^2.1.0",
|
||||
"koa-mock": "^1.6.2",
|
||||
"koa-proxy": "^0.9.0",
|
||||
"koa-rewrite": "^1.1.2",
|
||||
"koa-rt": "^1.0.0",
|
||||
"koa-safe-jsonp": "^0.3.1",
|
||||
"markdown-it": "^8.3.2",
|
||||
"mime": "^1.3.6",
|
||||
"mini-logger": "^1.1.3",
|
||||
"mkdirp": "^0.5.1",
|
||||
"moment": "^2.18.1",
|
||||
"mysql": "^2.14.1",
|
||||
"mz": "^2.6.0",
|
||||
"nodemailer": "^1.3.0",
|
||||
"normalize-registry-metadata": "^1.1.2",
|
||||
"rimraf": "^2.6.2",
|
||||
"semver": "^5.4.1",
|
||||
"sequelize": "^3.23.4",
|
||||
"thunkify-wrap": "^1.0.4",
|
||||
"treekill": "^1.0.0",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"urllib": "^2.24.0",
|
||||
"utility": "^1.12.0",
|
||||
"uuid": "^8.3.0",
|
||||
"xss": "^0.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autod": "*",
|
||||
"chunkstream": "*",
|
||||
"co-mocha": "*",
|
||||
"contributors": "*",
|
||||
"egg-ci": "^1.18.0",
|
||||
"intelli-espower-loader": "^1.0.1",
|
||||
"istanbul": "*",
|
||||
"jshint": "*",
|
||||
"mm": "*",
|
||||
"mocha": "*",
|
||||
"mocha": "3",
|
||||
"node-dev": "*",
|
||||
"pedding": "*",
|
||||
"pg": "~4.2.0",
|
||||
"pg-hstore": "~2.3.1",
|
||||
"should": "~4.0.4",
|
||||
"should-http": "*",
|
||||
"pg": "5",
|
||||
"pg-hstore": "2",
|
||||
"power-assert": "^1.4.2",
|
||||
"should": "8",
|
||||
"sqlite3": "*",
|
||||
"supertest": "*"
|
||||
"supertest": "2",
|
||||
"thunk-mocha": "1"
|
||||
},
|
||||
"homepage": "https://github.com/cnpm/cnpmjs.org",
|
||||
"repository": {
|
||||
@@ -81,8 +100,10 @@
|
||||
"web": "https://github.com/cnpm/cnpmjs.org"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/cnpm/cnpmjs.org/issues",
|
||||
"email": "fengmk2@gmail.com"
|
||||
"url": "https://github.com/cnpm/cnpmjs.org/issues"
|
||||
},
|
||||
"publishConfig": {
|
||||
"tag": "latest-3"
|
||||
},
|
||||
"keywords": [
|
||||
"cnpmjs.org",
|
||||
@@ -92,11 +113,23 @@
|
||||
"registry"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 1.0.0"
|
||||
"node": ">= 8.0.0"
|
||||
},
|
||||
"author": [
|
||||
"fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)",
|
||||
"fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)",
|
||||
"dead_horse <dead_horse@qq.com> (http://deadhorse.me)"
|
||||
],
|
||||
"ci": {
|
||||
"type": "github",
|
||||
"os": {
|
||||
"github": "linux, macos"
|
||||
},
|
||||
"npminstall": true,
|
||||
"version": "10, 12",
|
||||
"command": {
|
||||
"github": "ci"
|
||||
},
|
||||
"services": "mysql"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
|
||||
1
public/css/antd-0.9.1.min.css
vendored
Normal file
1
public/css/antd-0.9.1.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user