Compare commits
313 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7834f44901 | ||
|
|
463cf44133 | ||
|
|
e6bb4b76c2 | ||
|
|
b86d8eb110 | ||
|
|
070e72b571 | ||
|
|
6e6f87c147 | ||
|
|
bc9306eda7 | ||
|
|
66ea71756c | ||
|
|
0a1fff70cc | ||
|
|
71da60c16f | ||
|
|
b5d585988f | ||
|
|
758d1d4320 | ||
|
|
7b77f09264 | ||
|
|
7731b44ab4 | ||
|
|
4bc6998040 | ||
|
|
0d28fbde54 | ||
|
|
660035c1d5 | ||
|
|
f21e5a1c07 | ||
|
|
4d20d91965 | ||
|
|
a3e33850fc | ||
|
|
d888ef3297 | ||
|
|
ce4ec7e6e9 | ||
|
|
fe319b06ba | ||
|
|
d829600ed0 | ||
|
|
4dd59cb300 | ||
|
|
5ff25474c0 | ||
|
|
6d49a859c6 | ||
|
|
094178c3ca | ||
|
|
63a57b906e | ||
|
|
a09e6b142d | ||
|
|
bca9341e5d | ||
|
|
71b0662d49 | ||
|
|
b894c0fa2b | ||
|
|
4127fa1f07 | ||
|
|
5ee2a6e8fd | ||
|
|
8baa34145b | ||
|
|
4acb819356 | ||
|
|
2a84e39aee | ||
|
|
2019d72e36 | ||
|
|
b9fa7e7f98 | ||
|
|
694795e451 | ||
|
|
fae67880a5 | ||
|
|
0929748cbd | ||
|
|
a87945c6a0 | ||
|
|
d7345dcc76 | ||
|
|
1a9ea8c6cf | ||
|
|
8f0ada7bf8 | ||
|
|
ddd9c9557d | ||
|
|
9b2a846865 | ||
|
|
b1b238e20c | ||
|
|
26031a49d2 | ||
|
|
8bfc8ce71d | ||
|
|
dffae785ca | ||
|
|
078d7ddf3d | ||
|
|
8ec082db89 | ||
|
|
040b727878 | ||
|
|
02e1ba63d6 | ||
|
|
a16883618e | ||
|
|
7bd1c85d4e | ||
|
|
336e6f0da8 | ||
|
|
d43266670e | ||
|
|
8fdbbf0b04 | ||
|
|
b62a3b78fb | ||
|
|
d6cf093844 | ||
|
|
081f0cb4f8 | ||
|
|
0bf18639b6 | ||
|
|
cbe7a165b0 | ||
|
|
7cadfb4007 | ||
|
|
ec51fcf70c | ||
|
|
565a7d3cfe | ||
|
|
4ac857fba2 | ||
|
|
af7c952d2f | ||
|
|
6705172d4b | ||
|
|
c0295ecc77 | ||
|
|
aaf7f04d08 | ||
|
|
beceac10dc | ||
|
|
fad58273b6 | ||
|
|
e0361dc1f2 | ||
|
|
d8c337acc5 | ||
|
|
9d7683bd61 | ||
|
|
994b2e47aa | ||
|
|
b14faad3fb | ||
|
|
9163f793a3 | ||
|
|
1c0d4fdcdc | ||
|
|
20fce9f55f | ||
|
|
6b73a7762f | ||
|
|
fd34e0512c | ||
|
|
a84715167e | ||
|
|
db1cb0d0f8 | ||
|
|
dc137f0821 | ||
|
|
76d270fc77 | ||
|
|
b5190b56e8 | ||
|
|
d2d238db59 | ||
|
|
4c02d84fec | ||
|
|
74585f0b8a | ||
|
|
a6ac632e73 | ||
|
|
0b59547ac1 | ||
|
|
b5929c94e0 | ||
|
|
4c96682603 | ||
|
|
78af0fc15b | ||
|
|
ce381618a0 | ||
|
|
87486c484f | ||
|
|
4608712e05 | ||
|
|
3c6576bcab | ||
|
|
ba1986b931 | ||
|
|
5efa508376 | ||
|
|
38617d4572 | ||
|
|
a9a04b370d | ||
|
|
ec1d924dc1 | ||
|
|
234d1ec4d6 | ||
|
|
b8f4958d8f | ||
|
|
e56a21e890 | ||
|
|
e3434a58c5 | ||
|
|
cd9d38aadb | ||
|
|
0ff22ccc60 | ||
|
|
e4cfb13383 | ||
|
|
23ad3a29b4 | ||
|
|
f11b4e9954 | ||
|
|
6e775eed47 | ||
|
|
ca6841c042 | ||
|
|
652b900fc4 | ||
|
|
43379cc66d | ||
|
|
8f9b3db029 | ||
|
|
884b75f6a3 | ||
|
|
a958ee40c4 | ||
|
|
1a01909750 | ||
|
|
6901a5c994 | ||
|
|
33b13dc30e | ||
|
|
73d9d2c3d0 | ||
|
|
8b955adb42 | ||
|
|
1f219cbd1b | ||
|
|
17025d42b4 | ||
|
|
922f26b052 | ||
|
|
9371685188 | ||
|
|
ad0b66c7e5 | ||
|
|
5588880ec0 | ||
|
|
9ad72cbdcb | ||
|
|
4d033471fc | ||
|
|
590b7f7eee | ||
|
|
0ce817f9f3 | ||
|
|
59af2bfd56 | ||
|
|
a83f6bb183 | ||
|
|
b6f1531743 | ||
|
|
42d0b538ca | ||
|
|
a240eb9922 | ||
|
|
6853b73fb2 | ||
|
|
a887acec05 | ||
|
|
1d3f0f0ad3 | ||
|
|
20e04065df | ||
|
|
1cee298bc3 | ||
|
|
854bc6c9d8 | ||
|
|
b70c1c421a | ||
|
|
b1b6172892 | ||
|
|
e5a77a4368 | ||
|
|
b74cccd342 | ||
|
|
6aae538f49 | ||
|
|
74c8b25374 | ||
|
|
0cec2edea6 | ||
|
|
b59a0a99cf | ||
|
|
c7e82809e3 | ||
|
|
206ee505a8 | ||
|
|
d39838f930 | ||
|
|
512e21aaf5 | ||
|
|
f7904ee699 | ||
|
|
7007be6ef4 | ||
|
|
70aa3299e5 | ||
|
|
a04f5d68eb | ||
|
|
7cfe3b58ce | ||
|
|
afdf0bc653 | ||
|
|
42055ac91e | ||
|
|
b93e0dab41 | ||
|
|
01f2187830 | ||
|
|
c2f49fcdd9 | ||
|
|
3925ef6044 | ||
|
|
85494d2ba0 | ||
|
|
660ca394d6 | ||
|
|
ad7eeac00c | ||
|
|
f63b72891b | ||
|
|
28d31b093d | ||
|
|
466e14e92a | ||
|
|
3646c2ce50 | ||
|
|
fa44097d8f | ||
|
|
0c56294c47 | ||
|
|
41552fd27c | ||
|
|
cb4d4f51dd | ||
|
|
1b266d527c | ||
|
|
9d660be259 | ||
|
|
b041cc7361 | ||
|
|
e420f6985e | ||
|
|
60dc5cf5fa | ||
|
|
54916f49e5 | ||
|
|
a3cafaa297 | ||
|
|
aa13a100d4 | ||
|
|
3b29310826 | ||
|
|
6d76a590e5 | ||
|
|
3a98b63736 | ||
|
|
f5a2090fda | ||
|
|
989a69143f | ||
|
|
77e6db4f0e | ||
|
|
a3f7affe7b | ||
|
|
2cd0453c6b | ||
|
|
84bc126d57 | ||
|
|
760421661c | ||
|
|
425e430a3a | ||
|
|
ad3602bf15 | ||
|
|
2c5852b344 | ||
|
|
115a349fc5 | ||
|
|
3ceea3ccf4 | ||
|
|
ab6a9b2bea | ||
|
|
9cc84abbb5 | ||
|
|
c1c07cf2e4 | ||
|
|
ce5c97956b | ||
|
|
7cb7a517da | ||
|
|
c7df6f0f1a | ||
|
|
ee59176b67 | ||
|
|
f160d741cb | ||
|
|
94974f81ae | ||
|
|
41c0fea3dd | ||
|
|
dfae519e96 | ||
|
|
4d7d4f06b1 | ||
|
|
5a890ff27e | ||
|
|
ac124d0e2d | ||
|
|
7e267156eb | ||
|
|
258c34fea8 | ||
|
|
cd9d403237 | ||
|
|
6d43612044 | ||
|
|
2e99a779c7 | ||
|
|
43259ed126 | ||
|
|
51e32e3ece | ||
|
|
37d9d62d3f | ||
|
|
2e0a1ca3e8 | ||
|
|
5abaee4f2a | ||
|
|
5e2ca1d5e8 | ||
|
|
c0a6bb4869 | ||
|
|
2aba0836cf | ||
|
|
e1636e2c7d | ||
|
|
15245dc68b | ||
|
|
8dedabaa2d | ||
|
|
f4b82bbf0b | ||
|
|
78d81c02cd | ||
|
|
1c743f3759 | ||
|
|
041f80ab14 | ||
|
|
e5646c6553 | ||
|
|
6134983bcc | ||
|
|
7dab569d43 | ||
|
|
5aa84b03c6 | ||
|
|
77fc133ecb | ||
|
|
f98416e217 | ||
|
|
5f49b97aa4 | ||
|
|
8bdfd593c9 | ||
|
|
c214da39dc | ||
|
|
f2be453ef6 | ||
|
|
e2b7003749 | ||
|
|
944ac6a712 | ||
|
|
c502c00063 | ||
|
|
d536ad954b | ||
|
|
100706f311 | ||
|
|
8d8fd31a0e | ||
|
|
7aa2ea3086 | ||
|
|
5bf40164a1 | ||
|
|
e497fb354d | ||
|
|
e647d4a1fe | ||
|
|
33465ecf86 | ||
|
|
3eb3eb6be9 | ||
|
|
6ab1556ec0 | ||
|
|
9fd21a5c25 | ||
|
|
53b8398e93 | ||
|
|
c43b117e67 | ||
|
|
7db269e5f4 | ||
|
|
92ed8984d3 | ||
|
|
71d351da29 | ||
|
|
c8e1d8cc4a | ||
|
|
fd79879d0c | ||
|
|
354b82f6ea | ||
|
|
de0487cccc | ||
|
|
081ccd66df | ||
|
|
cebf501182 | ||
|
|
8fe593a182 | ||
|
|
8770458a46 | ||
|
|
559dc03cd4 | ||
|
|
70d12f235a | ||
|
|
aa7077e4b0 | ||
|
|
f5c19668f5 | ||
|
|
2b2a1904c6 | ||
|
|
a234c7e289 | ||
|
|
63562c30ec | ||
|
|
6968331b38 | ||
|
|
fc1a17e4d1 | ||
|
|
3872b7dfdf | ||
|
|
e340ce081a | ||
|
|
170a689168 | ||
|
|
7794d1099d | ||
|
|
bd8686c448 | ||
|
|
d8a76d03e0 | ||
|
|
4460810799 | ||
|
|
4124525eb8 | ||
|
|
ff50946cd3 | ||
|
|
3637eeefdd | ||
|
|
cecb41e6ea | ||
|
|
72b3240ef5 | ||
|
|
869d5681ce | ||
|
|
ff1bfd5acc | ||
|
|
7e3129c8b8 | ||
|
|
0618c732d7 | ||
|
|
9b34ab408c | ||
|
|
d990d3aa91 | ||
|
|
03ef728049 | ||
|
|
f00ed85d41 | ||
|
|
685af2a367 | ||
|
|
818f216fb4 | ||
|
|
1b47495565 | ||
|
|
0ab314f27c | ||
|
|
95076c8787 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -7,6 +7,7 @@ coverage.html
|
||||
*.pid
|
||||
*.gz
|
||||
dump.rdb
|
||||
.DS_Store
|
||||
|
||||
pids
|
||||
logs
|
||||
@@ -15,10 +16,15 @@ results
|
||||
node_modules
|
||||
npm-debug.log
|
||||
public/dist/
|
||||
.dist
|
||||
config/config.js
|
||||
backup/*.json
|
||||
backup/*.gz
|
||||
docs/web/history.md
|
||||
docs/web/_readme.md
|
||||
view/web/_layout.html
|
||||
bin/mysql.js
|
||||
bin/test.sql
|
||||
coverage/
|
||||
config/web_readme.md
|
||||
.tmp/
|
||||
|
||||
4
.jshintignore
Normal file
4
.jshintignore
Normal file
@@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
coverage/
|
||||
.tmp/
|
||||
.git/
|
||||
95
.jshintrc
Normal file
95
.jshintrc
Normal file
@@ -0,0 +1,95 @@
|
||||
{
|
||||
// JSHint Default Configuration File (as on JSHint website)
|
||||
// See http://jshint.com/docs/ for more details
|
||||
|
||||
"maxerr" : 50, // {int} Maximum error before stopping
|
||||
|
||||
// Enforcing
|
||||
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
|
||||
"camelcase" : false, // true: Identifiers must be in camelCase
|
||||
"curly" : true, // true: Require {} for every new block or scope
|
||||
"eqeqeq" : true, // true: Require triple equals (===) for comparison
|
||||
"forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty()
|
||||
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
|
||||
"indent" : false, // {int} Number of spaces to use for indentation
|
||||
"latedef" : false, // true: Require variables/functions to be defined before being used
|
||||
"newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
|
||||
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
|
||||
"noempty" : true, // true: Prohibit use of empty blocks
|
||||
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
|
||||
"plusplus" : false, // true: Prohibit use of `++` & `--`
|
||||
"quotmark" : false, // Quotation mark consistency:
|
||||
// false : do nothing (default)
|
||||
// true : ensure whatever is used is consistent
|
||||
// "single" : require single quotes
|
||||
// "double" : require double quotes
|
||||
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
|
||||
"unused" : false, // true: Require all defined variables be used
|
||||
"strict" : true, // true: Requires all functions run in ES5 Strict Mode
|
||||
"trailing" : false, // true: Prohibit trailing whitespaces
|
||||
"maxparams" : false, // {int} Max number of formal params allowed per function
|
||||
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
|
||||
"maxstatements" : false, // {int} Max number statements per function
|
||||
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
|
||||
"maxlen" : false, // {int} Max number of characters per line
|
||||
|
||||
// Relaxing
|
||||
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
|
||||
"boss" : true, // true: Tolerate assignments where comparisons would be expected
|
||||
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
|
||||
"eqnull" : false, // true: Tolerate use of `== null`
|
||||
"es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
|
||||
"esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`)
|
||||
"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
|
||||
// (ex: `for each`, multiple try/catch, function expression…)
|
||||
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
|
||||
"expr" : true, // true: Tolerate `ExpressionStatement` as Programs
|
||||
"funcscope" : false, // true: Tolerate defining variables inside control statements"
|
||||
"globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
|
||||
"iterator" : false, // true: Tolerate using the `__iterator__` property
|
||||
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
|
||||
"laxbreak" : true, // true: Tolerate possibly unsafe line breakings
|
||||
"laxcomma" : false, // true: Tolerate comma-first style coding
|
||||
"loopfunc" : false, // true: Tolerate functions being defined in loops
|
||||
"multistr" : true, // true: Tolerate multi-line strings
|
||||
"proto" : false, // true: Tolerate using the `__proto__` property
|
||||
"scripturl" : false, // true: Tolerate script-targeted URLs
|
||||
"smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment
|
||||
"shadow" : true, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
|
||||
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
|
||||
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
|
||||
"validthis" : true, // true: Tolerate using this in a non-constructor function
|
||||
|
||||
// Environments
|
||||
"browser" : true, // Web Browser (window, document, etc)
|
||||
"couch" : false, // CouchDB
|
||||
"devel" : true, // Development/debugging (alert, confirm, etc)
|
||||
"dojo" : false, // Dojo Toolkit
|
||||
"jquery" : false, // jQuery
|
||||
"mootools" : false, // MooTools
|
||||
"node" : true, // Node.js
|
||||
"nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
|
||||
"prototypejs" : false, // Prototype and Scriptaculous
|
||||
"rhino" : false, // Rhino
|
||||
"worker" : false, // Web Workers
|
||||
"wsh" : false, // Windows Scripting Host
|
||||
"yui" : false, // Yahoo User Interface
|
||||
"noyield" : true, // allow generators without a yield
|
||||
|
||||
// Legacy
|
||||
"nomen" : false, // true: Prohibit dangling `_` in variables
|
||||
"onevar" : false, // true: Allow only one `var` statement per function
|
||||
"passfail" : false, // true: Stop on first error
|
||||
"white" : false, // true: Check against strict whitespace and indentation rules
|
||||
|
||||
// Custom Globals
|
||||
"globals" : { // additional predefined global variables
|
||||
// mocha
|
||||
"describe": true,
|
||||
"it": true,
|
||||
"before": true,
|
||||
"afterEach": true,
|
||||
"beforeEach": true,
|
||||
"after": true
|
||||
}
|
||||
}
|
||||
@@ -8,5 +8,14 @@ logo.png
|
||||
public/dist/
|
||||
backup/*.json
|
||||
backup/*.gz
|
||||
docs/web/history.md
|
||||
docs/web/_readme.md
|
||||
view/web/_layout.html
|
||||
bin/mysql.js
|
||||
bin/test.sql
|
||||
coverage/
|
||||
.jshintrc
|
||||
.jshintignore
|
||||
.DS_Store
|
||||
config/web_readme.md
|
||||
.dist/
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- '0.11'
|
||||
script: make test-coveralls
|
||||
script: "make test-travis"
|
||||
after_script: "npm install coveralls@2 && cat ./coverage/lcov.info | coveralls"
|
||||
|
||||
2
AUTHORS
2
AUTHORS
@@ -1,5 +1,5 @@
|
||||
# Ordered by date of first contribution.
|
||||
# Auto-generated by 'contributors' on Fri, 24 Jan 2014 08:35:59 GMT.
|
||||
# Auto-generated by 'contributors' on Mon, 03 Mar 2014 13:01:28 GMT.
|
||||
# https://github.com/xingrz/node-contributors
|
||||
|
||||
fengmk2 <fengmk2@gmail.com> (https://github.com/fengmk2)
|
||||
|
||||
39
CONTRIBUTING.md
Normal file
39
CONTRIBUTING.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# How to contribute
|
||||
|
||||
Third-party patches are essential for keeping `cnpmjs.org` great.
|
||||
We want to keep it as easy as possible to contribute changes that
|
||||
get things working in your environment. There are a few guidelines that we
|
||||
need contributors to follow so that we can have a chance of keeping on
|
||||
top of things.
|
||||
|
||||
## Getting Started
|
||||
|
||||
* Make sure you have a [GitHub account](https://github.com/signup/free)
|
||||
* Fork the repository on GitHub
|
||||
|
||||
## Making Changes
|
||||
|
||||
* Create a topic branch from where you want to base your work.
|
||||
* This is usually the master branch.
|
||||
* Only target release branches if you are certain your fix must be on that
|
||||
branch.
|
||||
* To quickly create a topic branch based on master.
|
||||
Please avoid working directly on the `master` branch.
|
||||
* Make commits of logical units and including unit tests.
|
||||
* Check for unnecessary whitespace with `git diff --check` before committing.
|
||||
* Make sure your commit messages are in the proper format.
|
||||
* Make sure you have added the necessary tests for your changes.
|
||||
* Run _all_ the tests to assure nothing else was accidentally broken.
|
||||
* Follow [node style guide](https://github.com/felixge/node-style-guide)
|
||||
|
||||
## Submitting Changes
|
||||
|
||||
* Push your changes to a topic branch in your fork of the repository.
|
||||
* Submit a pull request.
|
||||
* Make sure travis-ci test pass.
|
||||
|
||||
# Additional Resources
|
||||
|
||||
* [General GitHub documentation](http://help.github.com/)
|
||||
* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
|
||||
* [cnpmjs.org](http://cnpmjs.org)
|
||||
519
History.md
519
History.md
@@ -1,19 +1,358 @@
|
||||
|
||||
0.3.2 / 2014-02-28
|
||||
1.1.0 / 2014-08-07
|
||||
==================
|
||||
|
||||
* Add user to maintainers when publish. fixed #395
|
||||
* List all npm registry api. close #413
|
||||
* limit list since
|
||||
* change deps by "~"
|
||||
* use cfork to make sure worker fork and restart
|
||||
* handle master uncaughtException. fixed #403
|
||||
|
||||
1.0.6 / 2014-08-02
|
||||
==================
|
||||
|
||||
* WTF moment@2.8.0 missing
|
||||
|
||||
1.0.5 / 2014-08-02
|
||||
==================
|
||||
|
||||
* unpublish pkg@version bug hotfix. fixed #400
|
||||
|
||||
1.0.4 / 2014-08-01
|
||||
==================
|
||||
|
||||
* hotfix #399 use not exists
|
||||
|
||||
1.0.3 / 2014-08-01
|
||||
==================
|
||||
|
||||
* add maintaining packages in user page
|
||||
|
||||
1.0.2 / 2014-08-01
|
||||
==================
|
||||
|
||||
* ~_~ fix auth error response message
|
||||
|
||||
1.0.1 / 2014-08-01
|
||||
==================
|
||||
|
||||
* Merge pull request #398 from cnpm/fix-auth
|
||||
* hot fix auth error
|
||||
|
||||
1.0.0 / 2014-08-01
|
||||
==================
|
||||
|
||||
* add private package list
|
||||
|
||||
0.9.2 / 2014-07-30
|
||||
==================
|
||||
|
||||
* hotfix save custom user bug
|
||||
|
||||
0.9.1 / 2014-07-30
|
||||
==================
|
||||
|
||||
* Handle user service auth throw custom error message
|
||||
* add test for config private packages
|
||||
* add config.privatePackages
|
||||
* add more comments in config/index.js
|
||||
|
||||
0.9.0 / 2014-07-29
|
||||
==================
|
||||
|
||||
* scopes init mv to services/user.js
|
||||
* show user more profile
|
||||
* registry show user support custom user service
|
||||
* support custom user service for user auth
|
||||
* remove session middleware
|
||||
* add DefaultUserService
|
||||
* check scopes in module.getAdapt
|
||||
* test public mode, fix some logic, close #382
|
||||
* move scope.js into publishable.js, add forcePublishWithScope
|
||||
* config.scopes not exist, means do not support scope
|
||||
* add assert scope middleware
|
||||
|
||||
0.8.7 / 2014-07-24
|
||||
==================
|
||||
|
||||
* fix unpublished info missing maintainers cause TypeError
|
||||
|
||||
0.8.6 / 2014-07-23
|
||||
==================
|
||||
|
||||
* show unpublished info on web package page. fixes #381
|
||||
|
||||
0.8.5 / 2014-07-22
|
||||
==================
|
||||
|
||||
* Only private package support default scoped. fixed #378
|
||||
|
||||
0.8.4 / 2014-07-22
|
||||
==================
|
||||
|
||||
* adapt default scpoe in /@:scope/:name/:version
|
||||
|
||||
0.8.3 / 2014-07-22
|
||||
==================
|
||||
|
||||
* hot fix download
|
||||
|
||||
0.8.2 / 2014-07-22
|
||||
==================
|
||||
|
||||
* fix default scope detect
|
||||
|
||||
0.8.1 / 2014-07-21
|
||||
==================
|
||||
|
||||
* add more test cases
|
||||
* support default @org. close #376
|
||||
* hotfix redis init error
|
||||
|
||||
0.8.0 / 2014-07-21
|
||||
==================
|
||||
|
||||
* support "scoped" packages. close #352
|
||||
* use safe jsonp
|
||||
* Stop support old publish flow. fix #368
|
||||
* update SQLs
|
||||
* use sync_info and sync_error categories
|
||||
* add categories to loggers. fix #370
|
||||
* fix get latest tag always not exists bug
|
||||
* support `npm publish --tag beta`. fix #366
|
||||
* use mini-logger and error-formater
|
||||
|
||||
0.7.0 / 2014-07-07
|
||||
==================
|
||||
|
||||
* use module_maintainers on GET /pakcage/:name page
|
||||
* use new module_maintainers on GET /:name
|
||||
* admin user should never publish to other user's packages. fix #363
|
||||
* Add a new table for module-maintainers.
|
||||
* gravatar use https
|
||||
* support https
|
||||
|
||||
0.6.1 / 2014-06-18
|
||||
==================
|
||||
|
||||
* hot fix removeTagsByNames()
|
||||
* fix _rev not exists
|
||||
* sync unpublished on GET /sync/:name
|
||||
|
||||
0.6.0 / 2014-06-16
|
||||
==================
|
||||
|
||||
* sync unpublished info. close #353
|
||||
* Delete not exists versions on sync worker. #353
|
||||
|
||||
0.5.3 / 2014-06-13
|
||||
==================
|
||||
|
||||
* fix sync response 204
|
||||
* add links in History.md
|
||||
* bump koa
|
||||
* fix test-cov
|
||||
* bump koa and should
|
||||
|
||||
0.5.2 / 2014-06-04
|
||||
==================
|
||||
|
||||
* sync hotfix
|
||||
* sync phantomjs downloads pkg. close [#348](https://github.com/cnpm/cnpmjs.org/issues/348)
|
||||
* add restart, fixed [#346](https://github.com/cnpm/cnpmjs.org/issues/346)
|
||||
|
||||
0.5.1 / 2014-05-28
|
||||
==================
|
||||
|
||||
* fix attack on /-/all/since?stale=update_after&startkey=2 close [#336](https://github.com/cnpm/cnpmjs.org/issues/336)
|
||||
* bump thunkify-wrap
|
||||
* bump koa-middlewares
|
||||
* remove outputError
|
||||
* bump dependencies
|
||||
* use svg badge
|
||||
* add package/notfound page
|
||||
* add dist mirror link to home page
|
||||
* fix sync listdiff and add more test cases
|
||||
|
||||
0.5.0 / 2014-05-13
|
||||
==================
|
||||
|
||||
* filter /nightlies/*
|
||||
* use koa setter instead of set()
|
||||
* add more info on error email
|
||||
* add sync dist to sync/index.js
|
||||
* show dist page
|
||||
* sync dist file and save it to database
|
||||
* disable gzip before [#335](https://github.com/cnpm/cnpmjs.org/issues/335) has fix
|
||||
|
||||
0.4.3 / 2014-04-18
|
||||
==================
|
||||
|
||||
* Merge pull request [#334](https://github.com/cnpm/cnpmjs.org/issues/334) from cnpm/fix-permission
|
||||
* add permission check to /:name/:tag
|
||||
* Merge pull request [#333](https://github.com/cnpm/cnpmjs.org/issues/333) from cnpm/issue332-tag
|
||||
* fix space
|
||||
* add put /:name/:tag, close [#332](https://github.com/cnpm/cnpmjs.org/issues/332)
|
||||
|
||||
0.4.2 / 2014-04-17
|
||||
==================
|
||||
|
||||
* sync interval config
|
||||
* fix fav ico and show pkg size on pkg info page. fix [#318](https://github.com/cnpm/cnpmjs.org/issues/318)
|
||||
* sync work sync one done must wait for a defer.setImmediate. fix [#328](https://github.com/cnpm/cnpmjs.org/issues/328)
|
||||
* bump dep versions
|
||||
* if download tarball 404, throw err better than ignore it. fixed [#325](https://github.com/cnpm/cnpmjs.org/issues/325)
|
||||
* refator sync
|
||||
* hotfix, close [#321](https://github.com/cnpm/cnpmjs.org/issues/321)
|
||||
* hotfix, close [#319](https://github.com/cnpm/cnpmjs.org/issues/319)
|
||||
* support custom web home page
|
||||
* npm get short only can read from cnpm now
|
||||
* if using reverted proxy like nginx, only binding on local host
|
||||
* fix redis detect logic
|
||||
|
||||
0.4.1 / 2014-04-10
|
||||
==================
|
||||
|
||||
* fix sync status code error
|
||||
|
||||
0.4.0 / 2014-04-09
|
||||
==================
|
||||
|
||||
* fix test cases to run on local machine
|
||||
* add contribute guidelines
|
||||
* use local mysql for dev env. fix [#308](https://github.com/cnpm/cnpmjs.org/issues/308)
|
||||
* use copy to
|
||||
* use koa-compress and koa-conditional-get
|
||||
* maintainers is string, fix [#301](https://github.com/cnpm/cnpmjs.org/issues/301)
|
||||
|
||||
0.3.13 / 2014-03-27
|
||||
==================
|
||||
|
||||
* fix npm adduser update 409 bug
|
||||
* fix multiline coverage
|
||||
* show package engines. fixed [#280](https://github.com/cnpm/cnpmjs.org/issues/280)
|
||||
* dont sync local package field. fix [#295](https://github.com/cnpm/cnpmjs.org/issues/295)
|
||||
|
||||
0.3.12 / 2014-03-26
|
||||
==================
|
||||
|
||||
* fix result.successes not exist error
|
||||
* fix search list
|
||||
* add simple request for listall
|
||||
* only return package name in /-/all and /-/all/since, fixed [#291](https://github.com/cnpm/cnpmjs.org/issues/291)
|
||||
* refine docs foloder
|
||||
* use module gmt_modified as etag. fix [#288](https://github.com/cnpm/cnpmjs.org/issues/288)
|
||||
* fix typo, remove unused config in package.json
|
||||
* web page only list cnpm registry related info
|
||||
* use generator in qnfs
|
||||
|
||||
0.3.11 / 2014-03-20
|
||||
==================
|
||||
|
||||
* use common.isMaintainer, fixed [#283](https://github.com/cnpm/cnpmjs.org/issues/283)
|
||||
* update dependencies
|
||||
* use co-mocha for test, fixed [#279](https://github.com/cnpm/cnpmjs.org/issues/279)
|
||||
* update thunkify-wrap, breaking change in thunkify-wrap
|
||||
* refactor SQLs by using multiline
|
||||
* use multiline to refactor sqls
|
||||
* ignore contributors
|
||||
|
||||
0.3.10 / 2014-03-16
|
||||
==================
|
||||
|
||||
* Only /_session request send the authSession. fixed [#223](https://github.com/cnpm/cnpmjs.org/issues/223)
|
||||
* sync npm user info when maintainers and contributors not exists. fixed [#82](https://github.com/cnpm/cnpmjs.org/issues/82)
|
||||
* save npm user to mysql
|
||||
* password salt always be randoms
|
||||
* remove session access in /name and /name/version, fixed [#274](https://github.com/cnpm/cnpmjs.org/issues/274)
|
||||
* fix update maintainer session error
|
||||
* update koa-middlewares
|
||||
* fix test, fix sync_by_install
|
||||
* use defer session
|
||||
* Support npm owner|author add [name] [pkg]. fixed [#271](https://github.com/cnpm/cnpmjs.org/issues/271)
|
||||
|
||||
0.3.9 / 2014-03-14
|
||||
==================
|
||||
|
||||
* custom user-agent
|
||||
* use co-urllib instead of thunkify urllib; fix mock http.request test cases
|
||||
* request limit custom message
|
||||
* add config.redis check
|
||||
* add koa-limit, fixed [#267](https://github.com/cnpm/cnpmjs.org/issues/267)
|
||||
|
||||
0.3.8 / 2014-03-11
|
||||
==================
|
||||
|
||||
* update middlewares, fixed missing charset bug [#264](https://github.com/cnpm/cnpmjs.org/issues/264)
|
||||
|
||||
0.3.7 / 2014-03-11
|
||||
==================
|
||||
|
||||
* show worker die date time
|
||||
* update to koa@0.5.1
|
||||
* hotfix for star user
|
||||
* fix yield gather, sync missing deps even no missing versions
|
||||
* fix return versions
|
||||
* fix makefile, remove eventproxy
|
||||
* refactor sync_module_worker
|
||||
* add make test-dev, fixed [#259](https://github.com/cnpm/cnpmjs.org/issues/259)
|
||||
* change npm.js to generator
|
||||
* update urllib, proxy/npm.js use generator
|
||||
* sync_all and sync_exist to generator
|
||||
* change function to generator
|
||||
* need node >= v0.11.9
|
||||
|
||||
0.3.6 / 2014-03-06
|
||||
==================
|
||||
|
||||
* install missing package should sync it from source npm. fixed [#252](https://github.com/cnpm/cnpmjs.org/issues/252)
|
||||
* npm publish dont contains .jshint*
|
||||
* npm test run jshint
|
||||
* Add jshint check: $ make jshint
|
||||
* use `yield* next` instead of `yield next`
|
||||
* replace dist.u.qiniudn.com with cnpmjs.org/dist
|
||||
|
||||
0.3.5 / 2014-03-05
|
||||
==================
|
||||
|
||||
* redirect /dist/xxx.tgz => http://dist.u.qiniudn.com/xxx.tgz fixed [#249](https://github.com/cnpm/cnpmjs.org/issues/249)
|
||||
* redirect /name to /package/name when /name is 404. fixed [#245](https://github.com/cnpm/cnpmjs.org/issues/245)
|
||||
* Add missing properies and sync missing star users. fixed [#235](https://github.com/cnpm/cnpmjs.org/issues/235)
|
||||
|
||||
0.3.4 / 2014-03-04
|
||||
==================
|
||||
|
||||
* add cov
|
||||
* use istanbul run test coverage
|
||||
* gzip support. fix [#241](https://github.com/cnpm/cnpmjs.org/issues/241)
|
||||
* readme spelling patch (@stanzheng)
|
||||
* default readme to null, fixed [#233](https://github.com/cnpm/cnpmjs.org/issues/233)
|
||||
* remove readme in versions
|
||||
|
||||
0.3.3 / 2014-02-28
|
||||
==================
|
||||
|
||||
* Merge pull request [#232](https://github.com/cnpm/cnpmjs.org/issues/232) from cnpm/host-hotfix
|
||||
* get request host from request.headers
|
||||
* Merge pull request [#231](https://github.com/cnpm/cnpmjs.org/issues/231) from cnpm/bug-fix
|
||||
* fix deps display bug[#230](https://github.com/cnpm/cnpmjs.org/issues/230) and nsf.url TypeError[#229](https://github.com/cnpm/cnpmjs.org/issues/229)
|
||||
|
||||
0.3.2 / 2014-02-28
|
||||
==================
|
||||
|
||||
* update koa-sess and koa-redis
|
||||
* fix sync all test
|
||||
* remove nfs.downloadStream first, fix tmppath error
|
||||
* fix fengmk2/giturl#1 bug
|
||||
* fix fengmk2/giturl[#1](https://github.com/cnpm/cnpmjs.org/issues/1) bug
|
||||
|
||||
0.3.1 / 2014-02-27
|
||||
0.3.1 / 2014-02-27
|
||||
==================
|
||||
|
||||
* add etag fixed #224
|
||||
* add etag fixed [#224](https://github.com/cnpm/cnpmjs.org/issues/224)
|
||||
* travis ci install on source npm
|
||||
|
||||
0.3.0 / 2014-02-27
|
||||
0.3.0 / 2014-02-27
|
||||
==================
|
||||
|
||||
* fix typo and dont sync not exists pkgs
|
||||
@@ -34,162 +373,162 @@
|
||||
* fix all the test of registry module.test.js
|
||||
* convert registry/module.js to koa type
|
||||
* fix auth middleware
|
||||
* finish registry user controller koa and update mm to support thunkify. fixed #196
|
||||
* finish registry user controller koa and update mm to support thunkify. fixed [#196](https://github.com/cnpm/cnpmjs.org/issues/196)
|
||||
* change controllers/user.js to koa
|
||||
* thunkify all proxy
|
||||
* convert all middlewares to koa type
|
||||
* change regsitry sync to koa
|
||||
* addd koa-jsonp, koa-bodyparser, fix / controller
|
||||
* first koa run registry home page /
|
||||
* Merge pull request #212 from cnpm/fix-sync-404
|
||||
* Merge pull request [#212](https://github.com/cnpm/cnpmjs.org/issues/212) from cnpm/fix-sync-404
|
||||
* return friendly 404 reason
|
||||
* Merge pull request #211 from cnpm/bug-fix
|
||||
* override json limit to default 10mb. fixed #209
|
||||
* fix #210 addPackageAndDist package version detect bug
|
||||
* Merge pull request [#211](https://github.com/cnpm/cnpmjs.org/issues/211) from cnpm/bug-fix
|
||||
* override json limit to default 10mb. fixed [#209](https://github.com/cnpm/cnpmjs.org/issues/209)
|
||||
* fix [#210](https://github.com/cnpm/cnpmjs.org/issues/210) addPackageAndDist package version detect bug
|
||||
|
||||
0.2.27 / 2014-02-19
|
||||
0.2.27 / 2014-02-19
|
||||
==================
|
||||
|
||||
* support json result in search, fixed #189
|
||||
* support json result in search, fixed [#189](https://github.com/cnpm/cnpmjs.org/issues/189)
|
||||
|
||||
0.2.26 / 2014-02-19
|
||||
0.2.26 / 2014-02-19
|
||||
==================
|
||||
|
||||
* npm publish also need to add deps
|
||||
|
||||
0.2.25 / 2014-02-19
|
||||
0.2.25 / 2014-02-19
|
||||
==================
|
||||
|
||||
* max handle number of package.json `dependencies` property
|
||||
* Dependents support. fixed #190
|
||||
* Dependents support. fixed [#190](https://github.com/cnpm/cnpmjs.org/issues/190)
|
||||
|
||||
0.2.24 / 2014-02-13
|
||||
0.2.24 / 2014-02-13
|
||||
==================
|
||||
|
||||
* fix if delete all the versions
|
||||
* refactor remove module, fixed #186
|
||||
* refactor remove module, fixed [#186](https://github.com/cnpm/cnpmjs.org/issues/186)
|
||||
|
||||
0.2.23 / 2014-01-26
|
||||
0.2.23 / 2014-01-26
|
||||
==================
|
||||
|
||||
* system admin can add, publish, remove the packages. fixed #176
|
||||
* system admin can add, publish, remove the packages. fixed [#176](https://github.com/cnpm/cnpmjs.org/issues/176)
|
||||
|
||||
0.2.22 / 2014-01-26
|
||||
0.2.22 / 2014-01-26
|
||||
==================
|
||||
|
||||
* add keyword and search support keyword. #181
|
||||
* add keyword and search support keyword. [#181](https://github.com/cnpm/cnpmjs.org/issues/181)
|
||||
|
||||
0.2.21 / 2014-01-24
|
||||
0.2.21 / 2014-01-24
|
||||
==================
|
||||
|
||||
* refactor code styles on package.html
|
||||
* nav-tabs e.preventDefault
|
||||
* Show registry server error response. fixed #178
|
||||
* Show registry server error response. fixed [#178](https://github.com/cnpm/cnpmjs.org/issues/178)
|
||||
* nav-tabs for package.html (@4simple)
|
||||
|
||||
0.2.20 / 2014-01-23
|
||||
0.2.20 / 2014-01-23
|
||||
==================
|
||||
|
||||
* hotfix sync missing dependencies and readmes
|
||||
* fix sync readme error, fixed #174
|
||||
* fix sync readme error, fixed [#174](https://github.com/cnpm/cnpmjs.org/issues/174)
|
||||
* add updateReadme in module
|
||||
|
||||
0.2.19 / 2014-01-22
|
||||
0.2.19 / 2014-01-22
|
||||
==================
|
||||
|
||||
* npm install no need to check authorization header. fixed #171
|
||||
* npm install no need to check authorization header. fixed [#171](https://github.com/cnpm/cnpmjs.org/issues/171)
|
||||
|
||||
0.2.18 / 2014-01-20
|
||||
0.2.18 / 2014-01-20
|
||||
==================
|
||||
|
||||
* Support gitlab git url to display and click. fixed #160
|
||||
* Support gitlab git url to display and click. fixed [#160](https://github.com/cnpm/cnpmjs.org/issues/160)
|
||||
* fix redis crash
|
||||
|
||||
0.2.17 / 2014-01-17
|
||||
0.2.17 / 2014-01-17
|
||||
==================
|
||||
|
||||
* custom logo url
|
||||
* hotfix layout bug
|
||||
|
||||
0.2.16 / 2014-01-16
|
||||
0.2.16 / 2014-01-16
|
||||
==================
|
||||
|
||||
* fix publish-time bug
|
||||
|
||||
0.2.15 / 2014-01-16
|
||||
0.2.15 / 2014-01-16
|
||||
==================
|
||||
|
||||
* add publish_time to debug
|
||||
|
||||
0.2.14 / 2014-01-16
|
||||
0.2.14 / 2014-01-16
|
||||
==================
|
||||
|
||||
* add make autod
|
||||
* update publish_time, fixed #163
|
||||
* update publish_time, fixed [#163](https://github.com/cnpm/cnpmjs.org/issues/163)
|
||||
|
||||
0.2.13 / 2014-01-15
|
||||
0.2.13 / 2014-01-15
|
||||
==================
|
||||
|
||||
* markdown tmpl not support footer, need to wrap on app start
|
||||
|
||||
0.2.12 / 2014-01-15
|
||||
0.2.12 / 2014-01-15
|
||||
==================
|
||||
|
||||
* add footer and npm client name customable
|
||||
|
||||
0.2.11 / 2014-01-15
|
||||
0.2.11 / 2014-01-15
|
||||
==================
|
||||
|
||||
* package page contributor link to search, default is true
|
||||
|
||||
0.2.10 / 2014-01-14
|
||||
0.2.10 / 2014-01-14
|
||||
==================
|
||||
|
||||
* fix #155 Content-Disposition wrong.
|
||||
* fix [#155](https://github.com/cnpm/cnpmjs.org/issues/155) Content-Disposition wrong.
|
||||
|
||||
0.2.9 / 2014-01-14
|
||||
0.2.9 / 2014-01-14
|
||||
==================
|
||||
|
||||
* support startkey=c and startkey="c"
|
||||
* support couch db search api. fixed #153
|
||||
* support couch db search api. fixed [#153](https://github.com/cnpm/cnpmjs.org/issues/153)
|
||||
* fix fork me image link
|
||||
* support sync by query.name
|
||||
|
||||
0.2.8 / 2014-01-14
|
||||
0.2.8 / 2014-01-14
|
||||
==================
|
||||
|
||||
* dont show err stack on test env
|
||||
* add download link for package page
|
||||
|
||||
0.2.7 / 2014-01-13
|
||||
0.2.7 / 2014-01-13
|
||||
==================
|
||||
|
||||
* add shasum when nfs.upload and hfs.uploadBuffer, fixed #148
|
||||
* add shasum when nfs.upload and hfs.uploadBuffer, fixed [#148](https://github.com/cnpm/cnpmjs.org/issues/148)
|
||||
|
||||
0.2.6 / 2014-01-13
|
||||
0.2.6 / 2014-01-13
|
||||
==================
|
||||
|
||||
* support custom session store, fixed #146
|
||||
* support custom session store, fixed [#146](https://github.com/cnpm/cnpmjs.org/issues/146)
|
||||
|
||||
0.2.5 / 2014-01-13
|
||||
0.2.5 / 2014-01-13
|
||||
==================
|
||||
|
||||
* add download timeout and unit test
|
||||
* use downloadStream() first
|
||||
* nfs download to a writeable stream.
|
||||
|
||||
0.2.4 / 2014-01-10
|
||||
0.2.4 / 2014-01-10
|
||||
==================
|
||||
|
||||
* set main script to index.js, fixed #142
|
||||
* set main script to index.js, fixed [#142](https://github.com/cnpm/cnpmjs.org/issues/142)
|
||||
|
||||
0.2.3 / 2014-01-10
|
||||
0.2.3 / 2014-01-10
|
||||
==================
|
||||
|
||||
* Dont show sync button on private package
|
||||
* Sync package as publish with no deps. fixed #138
|
||||
* Sync package as publish with no deps. fixed [#138](https://github.com/cnpm/cnpmjs.org/issues/138)
|
||||
|
||||
0.2.2 / 2014-01-10
|
||||
0.2.2 / 2014-01-10
|
||||
==================
|
||||
|
||||
* keep compatibility
|
||||
@@ -199,23 +538,23 @@
|
||||
* new npm publish in one request, add _publish_in_cnpm
|
||||
* support unsure name ufs
|
||||
* contributors maybe a object
|
||||
* Object #<Object> has no method 'forEach' fixed #134
|
||||
* support custom config as a module, fixed issue #132
|
||||
* support npm new publish flow. fixed #129
|
||||
* Object #<Object> has no method 'forEach' fixed [#134](https://github.com/cnpm/cnpmjs.org/issues/134)
|
||||
* support custom config as a module, fixed issue [#132](https://github.com/cnpm/cnpmjs.org/issues/132)
|
||||
* support npm new publish flow. fixed [#129](https://github.com/cnpm/cnpmjs.org/issues/129)
|
||||
* add toString and constructor to test admin
|
||||
* fix #119 hasOwnProperty check admin bug.
|
||||
* fix [#119](https://github.com/cnpm/cnpmjs.org/issues/119) hasOwnProperty check admin bug.
|
||||
|
||||
0.2.0 / 2013-12-27
|
||||
0.2.0 / 2013-12-27
|
||||
==================
|
||||
|
||||
* remove to lower case
|
||||
* fix #127 execSync and execsync.
|
||||
* fix [#127](https://github.com/cnpm/cnpmjs.org/issues/127) execSync and execsync.
|
||||
* add contributors list on package page
|
||||
* mv blanket to config
|
||||
* sync typeerror fix #statusCode
|
||||
* add disturl
|
||||
* fix #122 admin security bug
|
||||
* fixed #121, let pkg 404 as success
|
||||
* fix [#122](https://github.com/cnpm/cnpmjs.org/issues/122) admin security bug
|
||||
* fixed [#121](https://github.com/cnpm/cnpmjs.org/issues/121), let pkg 404 as success
|
||||
* fix sql insert error
|
||||
* fix typos
|
||||
|
||||
@@ -227,78 +566,78 @@
|
||||
* make sure all packages name are lower case
|
||||
* select ids from tag
|
||||
* fix nodejsctl
|
||||
* fix #112 missing versions and time no sync
|
||||
* fix [#112](https://github.com/cnpm/cnpmjs.org/issues/112) missing versions and time no sync
|
||||
* remove restart command
|
||||
* fix sync missing packages error
|
||||
* fix web/readme.md, add install
|
||||
* fix #109 pkg no times and no versions bug.
|
||||
* fix [#109](https://github.com/cnpm/cnpmjs.org/issues/109) pkg no times and no versions bug.
|
||||
|
||||
0.1.2 / 2013-12-19
|
||||
==================
|
||||
|
||||
* fix times not exists canot sync bug. fixed #101
|
||||
* fix times not exists canot sync bug. fixed [#101](https://github.com/cnpm/cnpmjs.org/issues/101)
|
||||
* support npm run command
|
||||
* remove before_install and install in travis, fixed #102
|
||||
* split all sub queries, fixed #104
|
||||
* fix doc, fixed #103
|
||||
* remove before_install and install in travis, fixed [#102](https://github.com/cnpm/cnpmjs.org/issues/102)
|
||||
* split all sub queries, fixed [#104](https://github.com/cnpm/cnpmjs.org/issues/104)
|
||||
* fix doc, fixed [#103](https://github.com/cnpm/cnpmjs.org/issues/103)
|
||||
* fix search too slow.
|
||||
* dont email sync log level info
|
||||
* only sync missing packages at first time
|
||||
* update dependencies
|
||||
* sync all will sync all the missing packages, fixed #97
|
||||
* sync all will sync all the missing packages, fixed [#97](https://github.com/cnpm/cnpmjs.org/issues/97)
|
||||
|
||||
0.1.0 / 2013-12-12
|
||||
==================
|
||||
|
||||
* add sync title
|
||||
* add favicon. fixed #69
|
||||
* refine sync page, fiexd #70
|
||||
* add favicon. fixed [#69](https://github.com/cnpm/cnpmjs.org/issues/69)
|
||||
* refine sync page, fiexd [#70](https://github.com/cnpm/cnpmjs.org/issues/70)
|
||||
* add app version
|
||||
* add test for sync
|
||||
* refine sync page
|
||||
* registry and web all use controllers/sync.js
|
||||
* sync from web, fixed #58
|
||||
* sync from web, fixed [#58](https://github.com/cnpm/cnpmjs.org/issues/58)
|
||||
* saving missing descriptions
|
||||
* add package download info. fixed #63
|
||||
* add package download info. fixed [#63](https://github.com/cnpm/cnpmjs.org/issues/63)
|
||||
* add avatar
|
||||
* use dependecies, fixed #issue62
|
||||
* support open search, fixed #60
|
||||
* make sure publish_time and author is same to source npm registry. fixed #56
|
||||
* support open search, fixed [#60](https://github.com/cnpm/cnpmjs.org/issues/60)
|
||||
* make sure publish_time and author is same to source npm registry. fixed [#56](https://github.com/cnpm/cnpmjs.org/issues/56)
|
||||
* add test for search
|
||||
* add a simple search by mysql like
|
||||
* fix This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery. fixed #54
|
||||
* fix This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery. fixed [#54](https://github.com/cnpm/cnpmjs.org/issues/54)
|
||||
* update install doc, use nodejsctl to start
|
||||
* must add limit on list by author sql
|
||||
* fix sql, change test to fit my local database, fixed #46
|
||||
* fix sql, change test to fit my local database, fixed [#46](https://github.com/cnpm/cnpmjs.org/issues/46)
|
||||
* use registry.cnpmjs.org
|
||||
* add install document and total package info on home page. fix #42
|
||||
* add module_id to tag table. #46
|
||||
* skip error version. fixed #43
|
||||
* add install document and total package info on home page. fix [#42](https://github.com/cnpm/cnpmjs.org/issues/42)
|
||||
* add module_id to tag table. [#46](https://github.com/cnpm/cnpmjs.org/issues/46)
|
||||
* skip error version. fixed [#43](https://github.com/cnpm/cnpmjs.org/issues/43)
|
||||
* sync may make a user do not exist in database, but have modules in registry
|
||||
* add user page
|
||||
* fix set license
|
||||
* ignore 404 on sync. fixed #39
|
||||
* ignore 404 on sync. fixed [#39](https://github.com/cnpm/cnpmjs.org/issues/39)
|
||||
* fix module page, add test
|
||||
* update urllib to 0.5.5
|
||||
* version and tag
|
||||
* add module page
|
||||
* fix download url
|
||||
* first get tag, then try version
|
||||
* support sync triggle by install, finish #31
|
||||
* support sync triggle by install, finish [#31](https://github.com/cnpm/cnpmjs.org/issues/31)
|
||||
* addTag error return 500
|
||||
* just one download field
|
||||
* add download total info on home page
|
||||
* add download count
|
||||
* versions empty and also check missing tags
|
||||
* remove tags on unpublish
|
||||
* add module tag. fix #6
|
||||
* add module tag. fix [#6](https://github.com/cnpm/cnpmjs.org/issues/6)
|
||||
* add [done] flag to check sync done on client
|
||||
* get sync log #29
|
||||
* get sync log [#29](https://github.com/cnpm/cnpmjs.org/issues/29)
|
||||
* fix test in module
|
||||
* rm tmp file on down request error
|
||||
* add time for debug str
|
||||
* fix pkg not exists null bug
|
||||
* use sync module woker to handle sync process. fixed #19
|
||||
* use sync module woker to handle sync process. fixed [#19](https://github.com/cnpm/cnpmjs.org/issues/19)
|
||||
* if private mode enable, only admin can publish module
|
||||
* add alias in readme
|
||||
* fix sql, add sort by name
|
||||
@@ -307,15 +646,15 @@
|
||||
* add npm and cnpm image
|
||||
* add registry total info on home page
|
||||
* fix mods bug in module.removeAll, change module.update => module.removeWithVersions
|
||||
* add test, fix bug. fixed #18
|
||||
* add test, fix bug. fixed [#18](https://github.com/cnpm/cnpmjs.org/issues/18)
|
||||
* spoort unpublish
|
||||
* add web page index readme
|
||||
* switchable nfs #21
|
||||
* switchable nfs [#21](https://github.com/cnpm/cnpmjs.org/issues/21)
|
||||
* change file path to match npm file path
|
||||
* use qn cdn to store tarball file fixed #16
|
||||
* add GET /:name/:version, fixed #3
|
||||
* use qn cdn to store tarball file fixed [#16](https://github.com/cnpm/cnpmjs.org/issues/16)
|
||||
* add GET /:name/:version, fixed [#3](https://github.com/cnpm/cnpmjs.org/issues/3)
|
||||
* add module controller test cases; fix next module not exists logic bug.
|
||||
* publish module flow finish #11
|
||||
* publish module flow finish [#11](https://github.com/cnpm/cnpmjs.org/issues/11)
|
||||
* add test for controllers/registry/user.js
|
||||
* add test for middleware/auth
|
||||
* add test for proxy/user
|
||||
@@ -326,9 +665,9 @@
|
||||
* add start time
|
||||
* add home page
|
||||
* remove session controller
|
||||
* adduser() finish fixed #5
|
||||
* adduser() finish fixed [#5](https://github.com/cnpm/cnpmjs.org/issues/5)
|
||||
* rm app.js and routes.js
|
||||
* Mock npm adduser server response, fixing #5
|
||||
* Mock npm adduser server response, fixing [#5](https://github.com/cnpm/cnpmjs.org/issues/5)
|
||||
* adjust project dir, separate registry and web server
|
||||
* Init rest frame for cnpmjs.org
|
||||
* init
|
||||
|
||||
68
Makefile
68
Makefile
@@ -1,39 +1,69 @@
|
||||
TESTS = $(shell ls -S `find test -type f -name "*.test.js" -print`)
|
||||
REPORTER = tap
|
||||
REPORTER = spec
|
||||
TIMEOUT = 30000
|
||||
MOCHA_OPTS =
|
||||
REGISTRY = --registry=https://registry.npm.taobao.org
|
||||
|
||||
install:
|
||||
@npm install --registry=http://registry.cnpmjs.org --cache=${HOME}/.npm/.cache/cnpm --disturl=http://dist.u.qiniudn.com
|
||||
@npm install $(REGISTRY) \
|
||||
--disturl=https://npm.taobao.org/dist
|
||||
|
||||
test: install
|
||||
@NODE_ENV=test ./node_modules/mocha/bin/mocha \
|
||||
--harmony-generators \
|
||||
jshint: install
|
||||
@-./node_modules/.bin/jshint ./
|
||||
|
||||
pretest:
|
||||
@mysql -uroot -e 'DROP DATABASE IF EXISTS cnpmjs_test;'
|
||||
@mysql -uroot -e 'CREATE DATABASE cnpmjs_test;'
|
||||
@mysql -uroot 'cnpmjs_test' < ./docs/db.sql
|
||||
@mysql -uroot 'cnpmjs_test' -e 'show tables;'
|
||||
|
||||
test: install pretest
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--harmony \
|
||||
--reporter $(REPORTER) \
|
||||
--timeout $(TIMEOUT) \
|
||||
--require should \
|
||||
--require should-http \
|
||||
--require co-mocha \
|
||||
--require ./test/init.js \
|
||||
$(MOCHA_OPTS) \
|
||||
$(TESTS)
|
||||
|
||||
test-cov:
|
||||
@$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=travis-cov
|
||||
test-cov cov: install pretest
|
||||
@NODE_ENV=test node --harmony \
|
||||
node_modules/.bin/istanbul cover --preserve-comments \
|
||||
./node_modules/.bin/_mocha \
|
||||
-- -u exports \
|
||||
--reporter $(REPORTER) \
|
||||
--timeout $(TIMEOUT) \
|
||||
--require should \
|
||||
--require should-http \
|
||||
--require co-mocha \
|
||||
--require ./test/init.js \
|
||||
$(MOCHA_OPTS) \
|
||||
$(TESTS)
|
||||
@./node_modules/.bin/cov coverage
|
||||
|
||||
test-cov-html:
|
||||
@rm -f coverage.html
|
||||
@$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=html-cov > coverage.html
|
||||
@ls -lh coverage.html
|
||||
|
||||
test-coveralls: test
|
||||
@echo TRAVIS_JOB_ID $(TRAVIS_JOB_ID)
|
||||
@-$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=mocha-lcov-reporter | ./node_modules/coveralls/bin/coveralls.js
|
||||
|
||||
test-all: test test-cov
|
||||
test-travis: install pretest
|
||||
@NODE_ENV=test node --harmony \
|
||||
node_modules/.bin/istanbul cover --preserve-comments \
|
||||
./node_modules/.bin/_mocha \
|
||||
--report lcovonly \
|
||||
-- \
|
||||
--reporter dot \
|
||||
--timeout $(TIMEOUT) \
|
||||
--require should \
|
||||
--require should-http \
|
||||
--require co-mocha \
|
||||
--require ./test/init.js \
|
||||
$(MOCHA_OPTS) \
|
||||
$(TESTS)
|
||||
|
||||
contributors: install
|
||||
@./node_modules/contributors/bin/contributors -f plain -o AUTHORS
|
||||
@./node_modules/.bin/contributors -f plain -o AUTHORS
|
||||
|
||||
autod: install
|
||||
@./node_modules/.bin/autod -w -e public,view,docs,backup
|
||||
@./node_modules/.bin/autod -w -e public,view,docs,backup,coverage -k nodemailer --prefix "~"
|
||||
@$(MAKE) install
|
||||
|
||||
.PHONY: test
|
||||
|
||||
112
README.md
112
README.md
@@ -1,37 +1,108 @@
|
||||
cnpmjs.org
|
||||
=======
|
||||
|
||||
[](http://travis-ci.org/cnpm/cnpmjs.org) [](https://coveralls.io/r/cnpm/cnpmjs.org) [](https://gemnasium.com/cnpm/cnpmjs.org)
|
||||
[![NPM version][npm-image]][npm-url]
|
||||
[![build status][travis-image]][travis-url]
|
||||
[![Test coverage][coveralls-image]][coveralls-url]
|
||||
[![Gittip][gittip-image]][gittip-url]
|
||||
[![David deps][david-image]][david-url]
|
||||
|
||||
[](https://nodei.co/npm/cnpmjs.org/)
|
||||
[npm-image]: https://img.shields.io/npm/v/cnpmjs.org.svg?style=flat
|
||||
[npm-url]: https://npmjs.org/package/cnpmjs.org
|
||||
[travis-image]: https://img.shields.io/travis/cnpm/cnpmjs.org.svg?style=flat
|
||||
[travis-url]: https://travis-ci.org/cnpm/cnpmjs.org
|
||||
[coveralls-image]: https://img.shields.io/coveralls/cnpm/cnpmjs.org.svg?style=flat
|
||||
[coveralls-url]: https://coveralls.io/r/cnpm/cnpmjs.org?branch=master
|
||||
[gittip-image]: https://img.shields.io/gittip/fengmk2.svg?style=flat
|
||||
[gittip-url]: https://www.gittip.com/fengmk2/
|
||||
[david-image]: https://img.shields.io/david/cnpm/cnpmjs.org.svg?style=flat
|
||||
[david-url]: https://david-dm.org/cnpm/cnpmjs.org
|
||||
|
||||

|
||||
|
||||
## What is this?
|
||||
|
||||
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).
|
||||
Private npm registry and web for Enterprise, base on [koa](http://koajs.com/),
|
||||
MySQL and [Simple Store Service](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide).
|
||||
|
||||
@[JacksonTian](https://github.com/JacksonTian/) had a talk about [private npm](https://speakerdeck.com/jacksontian/qi-ye-ji-node-dot-jskai-fa).
|
||||
Our goal is to provide a low cost maintenance and easy to use solution for private npm.
|
||||
|
||||
## 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.
|
||||
|
||||
### Features
|
||||
|
||||
* **Support "scoped" packages**: [npm/npm#5239](https://github.com/npm/npm/issues/5239)
|
||||
* **Simple to deploy**: only need `mysql` and a [simple store system](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide).
|
||||
You can get the source code through `npm` or `git`.
|
||||
* **Low cost and easy maintenance**: `package.json` info store in MySQL, tarball(tgz file) store in CDN or other store systems.
|
||||
* **Automatic synchronization**: automatic synchronization from any registry specified, support two sync modes:
|
||||
- Sync all modules from a specified registry, like [npm registry](http://registry.npmjs.org).
|
||||
- Only sync the modules that exists in your own registry.
|
||||
* **Manual synchronization**: automatic synchronization may has little delay, but you can syn immediately by manually.
|
||||
* **Customized client**: we provide a client [cnpm](https://github.com/cnpm/cnpm)
|
||||
to extend `npm` with more features(`sync` command, [gzip](https://github.com/npm/npm-registry-client/pull/40) support).
|
||||
And it easy to wrap for your own registry which build with `cnpmjs.org`.
|
||||
* **Compatible with NPM client**: you can use the origin NPM client with `cnpmjs.org`,
|
||||
only need to change the registry in config. Even include manual synchronization (through `install` command).
|
||||
|
||||
## Getting Start
|
||||
|
||||
* @[dead-horse](https://github.com/dead-horse): [What is cnpm?](http://deadhorse.me/slides/cnpmjs.html)
|
||||
* install and deploy cnpmjs.org through npm: [examples](https://github.com/cnpm/custom-cnpm-example)
|
||||
* Mirror NPM in China: [cnpmjs.org](http://cnpmjs.org)
|
||||
* cnpm client: [cnpm](https://github.com/cnpm/cnpm), `npm install -g cnpm`
|
||||
* [How to deploy cnpmjs.org](https://github.com/cnpm/cnpmjs.org/wiki/Deploy)
|
||||
* [NFS guide](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide)
|
||||
|
||||

|
||||
|
||||
## Develop on your local machine
|
||||
|
||||
## Install
|
||||
### Dependencies
|
||||
|
||||
* [node](http://nodejs.org) =0.11.12
|
||||
* [mysql](http://dev.mysql.com/downloads/) >= 0.5.0, include `mysqld` and `mysql cli`. I test on `mysql@5.6.16`.
|
||||
|
||||
### Start MySQL
|
||||
|
||||
```bash
|
||||
$ npm install --registry=http://r.cnpmjs.org --disturl=http://dist.u.qiniudn.com
|
||||
$ nohup mysqld &
|
||||
```
|
||||
|
||||
## Usage
|
||||
### Clone codes and run test
|
||||
|
||||
```js
|
||||
$ node --harmony-generators dispatch.js
|
||||
```bash
|
||||
# clone from git
|
||||
$ git clone https://github.com/cnpm/cnpmjs.org.git
|
||||
|
||||
# install dependencies
|
||||
$ make install
|
||||
|
||||
# test
|
||||
$ make test
|
||||
|
||||
# coverage
|
||||
$ make test-cov
|
||||
|
||||
# udpate dependencies
|
||||
$ make autod
|
||||
|
||||
# start server
|
||||
$ node --harmony_generators dispatch.js
|
||||
```
|
||||
|
||||
## Guide
|
||||
## How to contribute
|
||||
|
||||
* [How to deploy cnpmjs.org](https://github.com/cnpm/cnpmjs.org/wiki/Deploy)
|
||||
* [NFS guide](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide)
|
||||
* Clone the project
|
||||
* Checkout a new branch
|
||||
* Add new features or fix bugs in the new branch
|
||||
* Make a pull request and we will review it ASAP
|
||||
|
||||
Tips: make sure your code is following the [node-style-guide](https://github.com/felixge/node-style-guide).
|
||||
|
||||
## Authors
|
||||
|
||||
@@ -39,15 +110,16 @@ $ node --harmony-generators dispatch.js
|
||||
$ git summary
|
||||
|
||||
project : cnpmjs.org
|
||||
repo age : 3 months
|
||||
active : 145 days
|
||||
commits : 366
|
||||
files : 94
|
||||
repo age : 4 months ago
|
||||
commits : 472
|
||||
active : 167 days
|
||||
files : 104
|
||||
authors :
|
||||
217 fengmk2 59.3%
|
||||
146 dead_horse 39.9%
|
||||
2 4simple 0.5%
|
||||
1 Alsotang 0.3%
|
||||
272 fengmk2 57.6%
|
||||
195 dead_horse 41.3%
|
||||
2 4simple 0.4%
|
||||
2 Stanley Zheng 0.4%
|
||||
1 Alsotang 0.2%
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
@@ -7,7 +7,7 @@ export NODE_ENV='production'
|
||||
ulimit -c unlimited
|
||||
|
||||
cd `dirname $0`/..
|
||||
NODEJS='node --harmony-generators'
|
||||
NODEJS='node --harmony'
|
||||
BASE_HOME=`pwd`
|
||||
PROJECT_NAME=`basename ${BASE_HOME}`
|
||||
STDOUT_LOG=`$NODEJS -e "console.log(require('path').join(require('$BASE_HOME/config').logdir, 'nodejs_stdout.log'));\
|
||||
|
||||
103
common/logger.js
103
common/logger.js
@@ -15,73 +15,48 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var util = require('util');
|
||||
var moment = require('moment');
|
||||
var logstream = require('logfilestream');
|
||||
var ms = require('ms');
|
||||
var formater = require('error-formater');
|
||||
var Logger = require('mini-logger');
|
||||
var config = require('../config');
|
||||
var utility = require('utility');
|
||||
var mail = require('./mail');
|
||||
var util = require('util');
|
||||
|
||||
var isTEST = process.env.NODE_ENV === 'test';
|
||||
var ONE_DAY = ms('1d');
|
||||
var levels = ['info', 'warn', 'error'];
|
||||
var categories = ['sync_info', 'sync_error'];
|
||||
|
||||
levels.forEach(function (catetory) {
|
||||
var options = {
|
||||
logdir: config.logdir,
|
||||
duration: ONE_DAY,
|
||||
nameformat: '[' + catetory + '.]YYYY-MM-DD[.log]'
|
||||
};
|
||||
var stream = logstream(options);
|
||||
function write(msg) {
|
||||
var time = moment().format('YYYY-MM-DD HH:mm:ss.SSS');
|
||||
var subject = null;
|
||||
if (msg instanceof Error) {
|
||||
subject = msg.name;
|
||||
var err = {
|
||||
name: msg.name,
|
||||
code: msg.code,
|
||||
message: msg.message,
|
||||
stack: msg.stack,
|
||||
host: msg.host,
|
||||
url: msg.url,
|
||||
data: msg.data
|
||||
};
|
||||
if (err.name === 'Error' && typeof err.code === 'string') {
|
||||
err.name = err.code + err.name;
|
||||
}
|
||||
err.name += 'Exception';
|
||||
if (err.host) {
|
||||
err.message += ' (' + err.host + ')';
|
||||
}
|
||||
msg = util.format('%s nodejs.%s: %s\nURL: %s\nData: %j\n%s\n\n',
|
||||
time,
|
||||
err.name,
|
||||
err.stack,
|
||||
err.url,
|
||||
err.data,
|
||||
time
|
||||
);
|
||||
} else {
|
||||
msg = time + ' ' + util.format.apply(util, arguments) + '\n';
|
||||
}
|
||||
|
||||
if (!isTEST) {
|
||||
stream.write(msg);
|
||||
if (config.debug) {
|
||||
var level = catetory;
|
||||
console.log('[' + level + '] ' + msg);
|
||||
} else {
|
||||
if (catetory === 'error' && subject) {
|
||||
// send error email
|
||||
var to = [];
|
||||
for (var name in config.admins) {
|
||||
to.push(config.admins[name]);
|
||||
}
|
||||
mail.error(to, subject, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exports[catetory] = write;
|
||||
var logger = module.exports = Logger({
|
||||
categories: categories,
|
||||
dir: config.logdir,
|
||||
duration: '1d',
|
||||
format: '[{category}.]YYYY-MM-DD[.log]',
|
||||
stdout: config.debug && !isTEST,
|
||||
errorFormater: errorFormater
|
||||
});
|
||||
|
||||
var to = [];
|
||||
for (var user in config.admins) {
|
||||
to.push(config.admins[user]);
|
||||
}
|
||||
|
||||
function errorFormater(err) {
|
||||
var msg = formater.both(err);
|
||||
mail.error(to, msg.json.name, msg.text);
|
||||
return msg.text;
|
||||
}
|
||||
|
||||
logger.syncInfo = function () {
|
||||
var args = [].slice.call(arguments);
|
||||
if (typeof args[0] === 'string') {
|
||||
args[0] = util.format('[%s][%s] ', utility.logDate(), process.pid) + args[0];
|
||||
}
|
||||
logger.sync_info.apply(logger, args);
|
||||
};
|
||||
|
||||
logger.syncError =function () {
|
||||
var args = [].slice.call(arguments);
|
||||
if (typeof args[0] === 'string') {
|
||||
args[0] = util.format('[%s][%s] ', utility.logDate(), process.pid) + args[0];
|
||||
}
|
||||
logger.sync_error.apply(logger, arguments);
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var ready = require('ready');
|
||||
var mysql = require('mysql');
|
||||
var config = require('../config');
|
||||
@@ -35,6 +36,10 @@ var pool = mysql.createPool({
|
||||
exports.pool = pool;
|
||||
|
||||
exports.query = function (sql, values, cb) {
|
||||
if (typeof values === 'function') {
|
||||
cb = values;
|
||||
values = null;
|
||||
}
|
||||
pool.query(sql, values, function (err, rows) {
|
||||
cb(err, rows);
|
||||
});
|
||||
@@ -59,6 +64,8 @@ exports.escape = function (val) {
|
||||
|
||||
ready(exports);
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
function init() {
|
||||
exports.query('show tables', function (err, rows) {
|
||||
if (err) {
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var qn = require('qn');
|
||||
var fs = require('fs');
|
||||
var config = require('../config');
|
||||
var client = qn.create(config.qn);
|
||||
|
||||
thunkify(client, ['delete', 'uploadFile', 'upload', 'download']);
|
||||
|
||||
exports._client = client;
|
||||
|
||||
/**
|
||||
@@ -28,38 +31,53 @@ exports._client = client;
|
||||
* @param {Object} options
|
||||
* - {String} key
|
||||
* - {Number} size
|
||||
* @param {Function(err, result)} callback
|
||||
* - {Object} result
|
||||
* - {String} url
|
||||
*/
|
||||
exports.upload = function (filepath, options, callback) {
|
||||
client.delete(options.key, function (err, data) {
|
||||
client.uploadFile(filepath, {key: options.key, size: options.size}, function (err, data) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, {url: data.url});
|
||||
});
|
||||
exports.upload = function *(filepath, options) {
|
||||
try {
|
||||
yield client.delete(options.key);
|
||||
} catch (err) {
|
||||
// ignore error here
|
||||
}
|
||||
|
||||
var res = yield client.uploadFile(filepath, {
|
||||
key: options.key,
|
||||
size: options.size
|
||||
});
|
||||
var url = res && res[0] ? res[0].url : '';
|
||||
return { url: url };
|
||||
};
|
||||
|
||||
exports.uploadBuffer = function (buf, options, callback) {
|
||||
client.delete(options.key, function (err, data) {
|
||||
client.upload(buf, {key: options.key}, function (err, data) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, {url: data.url});
|
||||
});
|
||||
});
|
||||
exports.uploadBuffer = function *(buf, options) {
|
||||
try {
|
||||
yield client.delete(options.key);
|
||||
} catch (err) {
|
||||
// ignore error here
|
||||
}
|
||||
|
||||
var res = yield client.upload(buf, {key: options.key});
|
||||
var url = res && res[0] ? res[0].url : '';
|
||||
return { url: url };
|
||||
};
|
||||
|
||||
exports.url = function (key) {
|
||||
return client.resourceURL(key);
|
||||
};
|
||||
|
||||
exports.remove = function (key, callback) {
|
||||
client.delete(key, callback);
|
||||
exports.download = function* (key, filepath, options) {
|
||||
var writeStream = fs.createWriteStream(filepath);
|
||||
yield client.download(key, {
|
||||
timeout: options.timeout,
|
||||
writeStream: writeStream
|
||||
});
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
exports.remove = function *(key) {
|
||||
try {
|
||||
return yield client.delete(key);
|
||||
} catch (err) {
|
||||
if (err.name === 'QiniuFileNotExistsError') {
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
37
common/redis.js
Normal file
37
common/redis.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/**!
|
||||
* cnpmjs.org - common/redis.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var config = require('../config');
|
||||
|
||||
// close redis by set config.redis to `null` or `{}`
|
||||
if (config.redis && config.redis.host && config.redis.port) {
|
||||
|
||||
var redis = require('redis');
|
||||
var wrapper = require('co-redis');
|
||||
var logger = require('./logger');
|
||||
var _client = redis.createClient(config.redis.port, config.redis.host);
|
||||
|
||||
_client.on('error', function (err) {
|
||||
logger.error(err);
|
||||
});
|
||||
|
||||
module.exports = wrapper(_client);
|
||||
|
||||
} else {
|
||||
console.warn('[%s] [worker:%s:common/redis.js] Redis config can not found',
|
||||
Date(), process.pid);
|
||||
module.exports = null;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*!
|
||||
* cnpmjs.org - common/session.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var middlewares = require('koa-middlewares');
|
||||
var config = require('../config');
|
||||
|
||||
var key = 'AuthSession';
|
||||
var cookie = { path: '/', httpOnly: true, maxAge: 3600000 * 24 * 365, signed: false };
|
||||
var options = {
|
||||
key: key,
|
||||
cookie: cookie,
|
||||
};
|
||||
|
||||
if (!config.debug) {
|
||||
options.store = config.sessionStore || middlewares.RedisStore(config.redis);
|
||||
}
|
||||
|
||||
module.exports = middlewares.session(options);
|
||||
195
config/index.js
195
config/index.js
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - config/index.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -17,50 +17,71 @@
|
||||
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var os = require('os');
|
||||
var mkdirp = require('mkdirp');
|
||||
var copy = require('copy-to');
|
||||
|
||||
fs.existsSync = fs.existsSync || path.existsSync;
|
||||
var pkg = require('../package.json');
|
||||
var version = require('../package.json').version;
|
||||
|
||||
var root = path.dirname(__dirname);
|
||||
|
||||
var config = {
|
||||
version: pkg.version,
|
||||
|
||||
version: version,
|
||||
|
||||
/**
|
||||
* Cluster mode
|
||||
*/
|
||||
enableCluster: false,
|
||||
numCPUs: os.cpus().length,
|
||||
|
||||
/*
|
||||
* server configure
|
||||
*/
|
||||
registryPort: 7001,
|
||||
webPort: 7002,
|
||||
enableCluster: false,
|
||||
debug: true, // if debug
|
||||
logdir: path.join(root, '.tmp', 'logs'),
|
||||
viewCache: false,
|
||||
// mysql config
|
||||
mysqlServers: [
|
||||
{
|
||||
host: 'keydiary.mysql.rds.aliyuncs.com', // 'db4free.net'
|
||||
port: 3306,
|
||||
user: 'cnpmjs',
|
||||
password: 'cnpmjs123'
|
||||
}
|
||||
],
|
||||
mysqlDatabase: 'cnpmjstest',
|
||||
mysqlMaxConnections: 4,
|
||||
mysqlQueryTimeout: 5000,
|
||||
bindingHost: '127.0.0.1', // only binding on 127.0.0.1 for local access
|
||||
|
||||
// debug mode
|
||||
// if in debug mode, some middleware like limit wont load
|
||||
// logger module will print to stdout
|
||||
debug: true,
|
||||
// session secret
|
||||
sessionSecret: 'cnpmjs.org test session secret',
|
||||
redis: {
|
||||
host: 'pub-redis-19533.us-east-1-4.3.ec2.garantiadata.com',
|
||||
port: 19533,
|
||||
pass: 'cnpmjs_dev'
|
||||
},
|
||||
jsonLimit: '10mb', // max request json body size
|
||||
uploadDir: path.join(root, 'public', 'dist'),
|
||||
// qiniu cdn: http://www.qiniu.com/, it free for dev.
|
||||
qn: {
|
||||
accessKey: "iN7NgwM31j4-BZacMjPrOQBs34UG1maYCAQmhdCV",
|
||||
secretKey: "6QTOr2Jg1gcZEWDQXKOGZh5PziC2MCV5KsntT70j",
|
||||
bucket: "qtestbucket",
|
||||
domain: "http://qtestbucket.qiniudn.com"
|
||||
// max request json body size
|
||||
jsonLimit: '10mb',
|
||||
// log dir name
|
||||
logdir: path.join(root, '.tmp', 'logs'),
|
||||
// update file template dir
|
||||
uploadDir: path.join(root, '.dist'),
|
||||
// web page viewCache
|
||||
viewCache: false,
|
||||
|
||||
// config for koa-limit middleware
|
||||
// for limit download rates
|
||||
limit: {
|
||||
enable: false,
|
||||
token: 'koa-limit:download',
|
||||
limit: 1000,
|
||||
interval: 1000 * 60 * 60 * 24,
|
||||
whiteList: [],
|
||||
blackList: [],
|
||||
message: 'request frequency limited, any question, please contact fengmk2@gmail.com',
|
||||
},
|
||||
|
||||
enableCompress: false, // enable gzip response or not
|
||||
|
||||
// default system admins
|
||||
admins: {
|
||||
// name: email
|
||||
fengmk2: 'fengmk2@gmail.com',
|
||||
admin: 'admin@cnpmjs.org',
|
||||
dead_horse: 'dead_horse@qq.com',
|
||||
cnpmjstest10: 'cnpmjstest10@cnpmjs.org',
|
||||
},
|
||||
|
||||
// email notification for errors
|
||||
mail: {
|
||||
appname: 'cnpmjs.org',
|
||||
sender: 'cnpmjs.org mail sender <adderss@gmail.com>',
|
||||
@@ -72,33 +93,109 @@ var config = {
|
||||
debug: false
|
||||
},
|
||||
|
||||
logoURL: 'http://ww4.sinaimg.cn/large/69c1d4acgw1ebfly5kjlij208202oglr.jpg',
|
||||
registryHost: 'r.cnpmjs.org',
|
||||
|
||||
logoURL: '//ww4.sinaimg.cn/large/69c1d4acgw1ebfly5kjlij208202oglr.jpg', // cnpm logo image url
|
||||
customReadmeFile: '', // you can use your custom readme file instead the cnpm one
|
||||
customFooter: '', // you can add copyright and site total script html here
|
||||
npmClientName: 'cnpm', // use `${name} install package`
|
||||
packagePageContributorSearch: true, // package page contributor link to search, default is true
|
||||
sourceNpmRegistry: 'http://registry.npmjs.org',
|
||||
enablePrivate: true, // enable private mode, only admin can publish, other use just can sync package from source npm
|
||||
admins: {
|
||||
fengmk2: 'fengmk2@gmail.com',
|
||||
admin: 'admin@cnpmjs.org',
|
||||
dead_horse: 'dead_horse@qq.com',
|
||||
cnpmjstest10: 'cnpmjstest10@cnpmjs.org',
|
||||
|
||||
// max handle number of package.json `dependencies` property
|
||||
maxDependencies: 200,
|
||||
// backup filepath prefix
|
||||
backupFilePrefix: '/cnpm/backup/',
|
||||
|
||||
/**
|
||||
* mysql config
|
||||
*/
|
||||
|
||||
mysqlServers: [
|
||||
{
|
||||
host: '127.0.0.1',
|
||||
port: 3306,
|
||||
user: 'root',
|
||||
password: ''
|
||||
}
|
||||
],
|
||||
mysqlDatabase: 'cnpmjs_test',
|
||||
mysqlMaxConnections: 4,
|
||||
mysqlQueryTimeout: 5000,
|
||||
|
||||
|
||||
// redis config
|
||||
// use for koa-limit module as storage
|
||||
redis: null,
|
||||
|
||||
// package tarball store in qn by default
|
||||
// qiniu cdn: http://www.qiniu.com/, it free for dev.
|
||||
qn: {
|
||||
accessKey: "5UyUq-l6jsWqZMU6tuQ85Msehrs3Dr58G-mCZ9rE",
|
||||
secretKey: "YaRsPKiYm4nGUt8mdz2QxeV5Q_yaUzVxagRuWTfM",
|
||||
bucket: "qiniu-sdk-test",
|
||||
domain: "http://qiniu-sdk-test.qiniudn.com",
|
||||
},
|
||||
|
||||
// registry url name
|
||||
registryHost: 'r.cnpmjs.org',
|
||||
|
||||
|
||||
/**
|
||||
* registry mode config
|
||||
*/
|
||||
|
||||
// enable private mode, only admin can publish, other use just can sync package from source npm
|
||||
enablePrivate: true,
|
||||
|
||||
// registry scopes, if don't set, means do not support scopes
|
||||
scopes: [
|
||||
'@cnpm',
|
||||
'@cnpmtest'
|
||||
],
|
||||
|
||||
// redirect @cnpm/private-package => private-package
|
||||
// forward compatbility for update from lower version cnpmjs.org
|
||||
adaptScope: true,
|
||||
|
||||
// force user publish with scope
|
||||
// but admins still can publish without scope
|
||||
forcePublishWithScope: true,
|
||||
|
||||
// some registry already have some private packages in global scope
|
||||
// but we want to treat them as scoped private packages,
|
||||
// so you can use this white list.
|
||||
privatePackages: ['private-package'],
|
||||
|
||||
/**
|
||||
* sync configs
|
||||
*/
|
||||
|
||||
// sync dist config
|
||||
// sync node.js dist from nodejs.org
|
||||
noticeSyncDistError: true,
|
||||
disturl: 'http://nodejs.org/dist',
|
||||
syncDist: false,
|
||||
|
||||
// sync source
|
||||
sourceNpmRegistry: 'http://registry.npmjs.org',
|
||||
|
||||
// if install return 404, try to sync from source registry
|
||||
syncByInstall: true,
|
||||
backupFilePrefix: '/cnpm/backup/', // backup filepath prefix
|
||||
|
||||
// sync mode select
|
||||
// none: do not sync any module
|
||||
// exist: only sync exist modules
|
||||
// all: sync all modules
|
||||
syncModel: 'none', // 'none', 'all', 'exist'
|
||||
|
||||
syncConcurrency: 1,
|
||||
maxDependencies: 200, // max handle number of package.json `dependencies` property
|
||||
// sync interval, default is 10 minutes
|
||||
syncInterval: '10m',
|
||||
};
|
||||
|
||||
// load config/config.js, everything in config.js will cover the same key in index.js
|
||||
var customConfig = path.join(root, 'config/config.js');
|
||||
if (fs.existsSync(customConfig)) {
|
||||
var options = require(customConfig);
|
||||
for (var k in options) {
|
||||
config[k] = options[k];
|
||||
}
|
||||
copy(require(customConfig)).override(config);
|
||||
}
|
||||
|
||||
mkdirp.sync(config.logdir);
|
||||
@@ -110,7 +207,5 @@ config.loadConfig = function (customConfig) {
|
||||
if (!customConfig) {
|
||||
return;
|
||||
}
|
||||
for (var key in customConfig) {
|
||||
config[key] = customConfig[key];
|
||||
}
|
||||
copy(customConfig).override(config);
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,66 +15,190 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:registry');
|
||||
var logger = require('../../common/logger');
|
||||
var debug = require('debug')('cnpmjs.org:controllers:registry:user');
|
||||
var utility = require('utility');
|
||||
var crypto = require('crypto');
|
||||
var UserService = require('../../services/user');
|
||||
var User = require('../../proxy/user');
|
||||
var config = require('../../config');
|
||||
var common = require('../../lib/common');
|
||||
|
||||
exports.show = function *(next) {
|
||||
exports.show = function* (next) {
|
||||
var name = this.params.name;
|
||||
var user = yield User.get(name);
|
||||
if (!user) {
|
||||
return yield next;
|
||||
var isAdmin = common.isAdmin(name);
|
||||
var scopes = config.scopes || [];
|
||||
if (config.customUserService) {
|
||||
var customUser = yield* UserService.get(name);
|
||||
if (customUser) {
|
||||
isAdmin = !!customUser.site_admin;
|
||||
scopes = customUser.scopes;
|
||||
|
||||
var data = {
|
||||
user: customUser
|
||||
};
|
||||
yield* User.saveCustomUser(data);
|
||||
}
|
||||
}
|
||||
this.etag = '"' + user.rev + '"';
|
||||
var data = {
|
||||
_id: 'org.couchdb.user:' + user.name,
|
||||
_rev: user.rev,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
type: 'user',
|
||||
roles: [],
|
||||
date: user.gmt_modified,
|
||||
|
||||
var user = yield* User.get(name);
|
||||
if (!user) {
|
||||
return yield* next;
|
||||
}
|
||||
|
||||
var data = user.json;
|
||||
if (!data) {
|
||||
data = {
|
||||
_id: 'org.couchdb.user:' + user.name,
|
||||
_rev: user.rev,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
type: 'user',
|
||||
roles: [],
|
||||
date: user.gmt_modified,
|
||||
};
|
||||
}
|
||||
|
||||
if (data.login) {
|
||||
// custom user format
|
||||
// convert to npm user format
|
||||
data = {
|
||||
_id: 'org.couchdb.user:' + user.name,
|
||||
_rev: user.rev,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
type: 'user',
|
||||
roles: [],
|
||||
date: user.gmt_modified,
|
||||
avatar: data.avatar_url,
|
||||
fullname: data.name || data.login,
|
||||
homepage: data.html_url,
|
||||
};
|
||||
}
|
||||
|
||||
data._cnpm_meta = {
|
||||
id: user.id,
|
||||
npm_user: user.npm_user === 1,
|
||||
custom_user: user.npm_user === 2,
|
||||
gmt_create: user.gmt_create,
|
||||
gmt_modified: user.gmt_modified,
|
||||
admin: isAdmin,
|
||||
scopes: scopes,
|
||||
};
|
||||
|
||||
this.body = data;
|
||||
};
|
||||
|
||||
function ensurePasswordSalt(user, body) {
|
||||
if (!user.password_sha && body.password) {
|
||||
// create password_sha on server
|
||||
user.salt = crypto.randomBytes(30).toString('hex');
|
||||
user.password_sha = utility.sha1(body.password + user.salt);
|
||||
}
|
||||
}
|
||||
|
||||
// npm 1.4.4
|
||||
// add new user first
|
||||
// @see https://github.com/npm/npm-registry-client/commit/effb4bc88d443f764f2c2e8b4dd583cc72cf6084
|
||||
// PUT /-/user/org.couchdb.user:mk2 { accept: 'application/json',
|
||||
// 'accept-encoding': 'gzip',
|
||||
// 'user-agent': 'node/v0.11.12 darwin x64',
|
||||
// host: '127.0.0.1:7001',
|
||||
// 'content-type': 'application/json',
|
||||
// 'content-length': '150',
|
||||
// connection: 'close' } { name: 'mk2',
|
||||
// password: '123456',
|
||||
// email: 'fengmk2@gmail.com',
|
||||
// _id: 'org.couchdb.user:mk2',
|
||||
// type: 'user',
|
||||
// roles: [],
|
||||
// date: '2014-03-15T02:33:19.465Z' }
|
||||
|
||||
// old npm flow
|
||||
// json:
|
||||
// { name: 'fengmk2',
|
||||
// salt: 'xxxx',
|
||||
// password_sha: 'xxxxxx',
|
||||
// email: 'fengmk2@gmail.com',
|
||||
// _id: 'org.couchdb.user:fengmk2',
|
||||
// type: 'user',
|
||||
// roles: [],
|
||||
// date: '2013-12-04T12:56:13.714Z' } }
|
||||
exports.add = function *() {
|
||||
// PUT /-/user/org.couchdb.user:mk2 { accept: 'application/json',
|
||||
// 'user-agent': 'node/v0.8.26 darwin x64',
|
||||
// host: '127.0.0.1:7001',
|
||||
// 'content-type': 'application/json',
|
||||
// 'content-length': '258',
|
||||
// connection: 'keep-alive' }
|
||||
// { name: 'mk2',
|
||||
// salt: '12351936478446a5466d4fb1633b80f3838b4caaa03649a885ac722cd6',
|
||||
// password_sha: '123408912a6db1d96b132a90856d99db029cef3d',
|
||||
// email: 'fengmk2@gmail.com',
|
||||
// _id: 'org.couchdb.user:mk2',
|
||||
// type: 'user',
|
||||
// roles: [],
|
||||
// date: '2014-03-15T02:39:25.696Z' }
|
||||
exports.add = function* () {
|
||||
var name = this.params.name;
|
||||
var body = this.request.body || {};
|
||||
var user = {
|
||||
name: body.name,
|
||||
salt: body.salt,
|
||||
password_sha: body.password_sha,
|
||||
// salt: body.salt,
|
||||
// password_sha: body.password_sha,
|
||||
email: body.email,
|
||||
ip: this.ip || '0.0.0.0',
|
||||
// roles: body.roles || [],
|
||||
};
|
||||
|
||||
if (!user.name || !user.salt || !user.password_sha || !user.email) {
|
||||
ensurePasswordSalt(user, body);
|
||||
|
||||
if (!body.password || !user.name || !user.salt || !user.password_sha || !user.email) {
|
||||
this.status = 422;
|
||||
this.body = {
|
||||
error: 'paramError',
|
||||
reason: 'params missing'
|
||||
reason: 'params missing, name, email or password missing.'
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
debug('add user: %j', body);
|
||||
|
||||
var loginedUser;
|
||||
try {
|
||||
loginedUser = yield UserService.auth(body.name, body.password);
|
||||
} catch (err) {
|
||||
this.status = err.status || 500;
|
||||
this.body = {
|
||||
error: err.name,
|
||||
reason: err.message
|
||||
};
|
||||
return;
|
||||
}
|
||||
if (loginedUser) {
|
||||
var rev = Date.now() + '-' + loginedUser.login;
|
||||
if (config.customUserService) {
|
||||
// make sure sync user meta to cnpm database
|
||||
var data = user;
|
||||
data.rev = rev;
|
||||
data.user = loginedUser;
|
||||
yield* User.saveCustomUser(data);
|
||||
}
|
||||
this.status = 201;
|
||||
this.body = {
|
||||
ok: true,
|
||||
id: 'org.couchdb.user:' + loginedUser.login,
|
||||
rev: rev,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.customUserService) {
|
||||
// user login fail, not allow to add new user
|
||||
this.status = 401;
|
||||
this.body = {
|
||||
error: 'unauthorized',
|
||||
reason: 'Login fail, please check your login name and password'
|
||||
};
|
||||
return;
|
||||
}
|
||||
debug('add user: %j', user);
|
||||
|
||||
var existUser = yield User.get(name);
|
||||
if (existUser) {
|
||||
this.status = 409;
|
||||
this.body = {
|
||||
error: 'conflict',
|
||||
reason: 'Document update conflict.'
|
||||
reason: 'User ' + name + ' already exists.'
|
||||
};
|
||||
return;
|
||||
}
|
||||
@@ -89,35 +213,33 @@ exports.add = function *() {
|
||||
};
|
||||
};
|
||||
|
||||
exports.authSession = function *() {
|
||||
// body: {"name":"foo","password":"****"}
|
||||
var body = this.request.body || {};
|
||||
var name = body.name;
|
||||
var password = body.password;
|
||||
var user = yield User.auth(name, password);
|
||||
debug('authSession %s: %j', name, user);
|
||||
|
||||
if (!user) {
|
||||
this.status = 401;
|
||||
this.body = {ok: false, name: null, roles: []};
|
||||
return;
|
||||
}
|
||||
|
||||
this.session.name = user.name;
|
||||
this.body = {ok: true, name: user.name, roles: []};
|
||||
};
|
||||
|
||||
// logined before update, no need to auth user again
|
||||
// { name: 'admin',
|
||||
// password: '123123',
|
||||
// email: 'fengmk2@gmail.com',
|
||||
// _id: 'org.couchdb.user:admin',
|
||||
// type: 'user',
|
||||
// roles: [],
|
||||
// date: '2014-08-05T16:08:22.645Z',
|
||||
// _rev: '1-1a18c3d73ba42e863523a399ff3304d8',
|
||||
// _cnpm_meta:
|
||||
// { id: 14,
|
||||
// npm_user: false,
|
||||
// custom_user: false,
|
||||
// gmt_create: '2014-08-05T15:46:58.000Z',
|
||||
// gmt_modified: '2014-08-05T15:46:58.000Z',
|
||||
// admin: true,
|
||||
// scopes: [ '@cnpm', '@cnpmtest' ] } }
|
||||
exports.update = function *(next) {
|
||||
var name = this.params.name;
|
||||
var rev = this.params.rev;
|
||||
if (!name || !rev) {
|
||||
return yield next;
|
||||
return yield* next;
|
||||
}
|
||||
debug('update: %s, rev: %s, user.name: %s', name, rev, this.user.name);
|
||||
|
||||
debug('update: %s, rev: %s, session.name: %s', name, rev, this.session.name);
|
||||
|
||||
if (name !== this.session.name) {
|
||||
// must authSession first
|
||||
if (name !== this.user.name) {
|
||||
// must auth user first
|
||||
this.status = 401;
|
||||
this.body = {
|
||||
error: 'unauthorized',
|
||||
@@ -129,13 +251,27 @@ exports.update = function *(next) {
|
||||
var body = this.request.body || {};
|
||||
var user = {
|
||||
name: body.name,
|
||||
salt: body.salt,
|
||||
password_sha: body.password_sha,
|
||||
// salt: body.salt,
|
||||
// password_sha: body.password_sha,
|
||||
email: body.email,
|
||||
ip: this.ip || '0.0.0.0',
|
||||
rev: body.rev || body._rev,
|
||||
// roles: body.roles || [],
|
||||
};
|
||||
|
||||
debug('update user %j', body);
|
||||
|
||||
ensurePasswordSalt(user, body);
|
||||
|
||||
if (!body.password || !user.name || !user.salt || !user.password_sha || !user.email) {
|
||||
this.status = 422;
|
||||
this.body = {
|
||||
error: 'paramError',
|
||||
reason: 'params missing, name, email or password missing.'
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
var result = yield User.update(user);
|
||||
if (!result) {
|
||||
this.status = 409;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/download.js
|
||||
* cnpmjs.org - controllers/sync.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
@@ -14,15 +14,17 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:sync');
|
||||
var Log = require('../proxy/module_log');
|
||||
var SyncModuleWorker = require('../proxy/sync_module_worker');
|
||||
|
||||
exports.sync = function *() {
|
||||
var username = this.session.name || 'anonymous';
|
||||
var name = this.params.name;
|
||||
exports.sync = function* () {
|
||||
var username = this.user.name || 'anonymous';
|
||||
var name = this.params.name || this.params[0];
|
||||
var publish = this.query.publish === 'true';
|
||||
var noDep = this.query.nodeps === 'true';
|
||||
if (publish && !this.session.isAdmin) {
|
||||
debug('sync %s with query: %j', name, this.query);
|
||||
if (publish && !this.user.isAdmin) {
|
||||
this.status = 403;
|
||||
this.body = {
|
||||
error: 'no_perms',
|
||||
@@ -37,9 +39,10 @@ exports.sync = function *() {
|
||||
};
|
||||
|
||||
var result = yield SyncModuleWorker.sync(name, username, options);
|
||||
debug('sync %s got %j', name, result);
|
||||
|
||||
// friendly 404 reason info
|
||||
if (result.staticCache === 404) {
|
||||
if (result.statusCode === 404) {
|
||||
this.status = 404;
|
||||
this.body = {
|
||||
ok: false,
|
||||
@@ -48,7 +51,7 @@ exports.sync = function *() {
|
||||
return;
|
||||
}
|
||||
if (!result.ok) {
|
||||
this.status = result.statusCode;
|
||||
this.status = result.statusCode || 500;
|
||||
this.body = result.pkg;
|
||||
return;
|
||||
}
|
||||
@@ -59,13 +62,13 @@ exports.sync = function *() {
|
||||
};
|
||||
};
|
||||
|
||||
exports.getSyncLog = function *(next) {
|
||||
var logId = this.params.id;
|
||||
var name = this.params.name;
|
||||
exports.getSyncLog = function* (next) {
|
||||
// params: [$name, $id] on scope package
|
||||
var logId = this.params.id || this.params[1];
|
||||
var offset = Number(this.query.offset) || 0;
|
||||
var row = yield Log.get(logId);
|
||||
if (!row) {
|
||||
return yield next;
|
||||
return yield* next;
|
||||
}
|
||||
|
||||
var log = row.log.trim();
|
||||
|
||||
@@ -15,13 +15,12 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var microtime = require('microtime');
|
||||
var Total = require('../proxy/total');
|
||||
var Download = require('./download');
|
||||
var version = require('../package.json').version;
|
||||
var config = require('../config');
|
||||
|
||||
var startTime = '' + microtime.now();
|
||||
var startTime = '' + Date.now();
|
||||
|
||||
exports.show = function *() {
|
||||
var r = yield [Total.get(), Download.total()];
|
||||
|
||||
46
controllers/utils.js
Normal file
46
controllers/utils.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/**!
|
||||
* 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('ms');
|
||||
var nfs = require('../common/nfs');
|
||||
var config = require('../config');
|
||||
|
||||
var DOWNLOAD_TIMEOUT = ms('10m');
|
||||
|
||||
exports.downloadAsReadStream = function* (key) {
|
||||
var tmpPath = path.join(config.uploadDir,
|
||||
utility.randomString() + key.replace(/\//g, '-'));
|
||||
function cleanup() {
|
||||
debug('cleanup %s', tmpPath);
|
||||
fs.unlink(tmpPath, utility.noop);
|
||||
}
|
||||
debug('downloadAsReadStream() %s to %s', key, tmpPath);
|
||||
try {
|
||||
yield nfs.download(key, tmpPath, {timeout: DOWNLOAD_TIMEOUT});
|
||||
} catch (err) {
|
||||
debug('downloadAsReadStream() %s to %s error: %s', key, tmpPath, err.stack);
|
||||
cleanup();
|
||||
throw err;
|
||||
}
|
||||
var tarball = fs.createReadStream(tmpPath);
|
||||
tarball.once('error', cleanup);
|
||||
tarball.once('end', cleanup);
|
||||
return tarball;
|
||||
};
|
||||
85
controllers/web/dist.js
Normal file
85
controllers/web/dist.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/web/dist.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:web:dist');
|
||||
var mime = require('mime');
|
||||
var Dist = require('../../proxy/dist');
|
||||
var config = require('../../config');
|
||||
var downloadAsReadStream = require('../utils').downloadAsReadStream;
|
||||
|
||||
function padding(max, current, pad) {
|
||||
pad = pad || ' ';
|
||||
var left = max - current;
|
||||
var str = '';
|
||||
for (var i = 0; i < left; i++) {
|
||||
str += pad;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
exports.list = function* (next) {
|
||||
var params = this.params;
|
||||
var url = params[0];
|
||||
if (!url) {
|
||||
// GET /dist => /dist/
|
||||
return this.redirect('/dist/');
|
||||
}
|
||||
|
||||
var isDir = url[url.length - 1] === '/';
|
||||
if (!isDir) {
|
||||
return yield* download.call(this, next);
|
||||
}
|
||||
|
||||
var items = yield* Dist.listdir(url);
|
||||
if (url === '/') {
|
||||
// phantomjs/
|
||||
items.push({
|
||||
name: 'phantomjs/',
|
||||
date: '',
|
||||
});
|
||||
}
|
||||
|
||||
yield this.render('dist', {
|
||||
title: 'Mirror index of ' + config.disturl + url,
|
||||
disturl: config.disturl,
|
||||
dirname: url,
|
||||
items: items,
|
||||
padding: padding
|
||||
});
|
||||
};
|
||||
|
||||
function* download(next) {
|
||||
var fullname = this.params[0];
|
||||
var info = yield* Dist.getfile(fullname);
|
||||
debug('download %s got %j', fullname, info);
|
||||
if (!info || !info.url) {
|
||||
return yield* next;
|
||||
}
|
||||
|
||||
if (info.url.indexOf('http') === 0) {
|
||||
return this.redirect(info.url);
|
||||
}
|
||||
|
||||
// download it from nfs
|
||||
if (typeof info.size === 'number') {
|
||||
this.length = info.size;
|
||||
}
|
||||
this.type = mime.lookup(info.url);
|
||||
this.attachment = info.name;
|
||||
this.etag = info.sha1;
|
||||
|
||||
this.body = yield* downloadAsReadStream(info.url);
|
||||
}
|
||||
@@ -14,6 +14,8 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:controllers:web:package');
|
||||
var bytes = require('bytes');
|
||||
var giturl = require('giturl');
|
||||
var moment = require('moment');
|
||||
var eventproxy = require('eventproxy');
|
||||
@@ -28,11 +30,18 @@ var sync = require('../sync');
|
||||
var Log = require('../../proxy/module_log');
|
||||
var ModuleDeps = require('../../proxy/module_deps');
|
||||
var setDownloadURL = require('../../lib/common').setDownloadURL;
|
||||
var ModuleStar = require('../../proxy/module_star');
|
||||
var packageService = require('../../services/package');
|
||||
var ModuleUnpublished = require('../../proxy/module_unpublished');
|
||||
|
||||
exports.display = function *(next) {
|
||||
exports.display = function* (next) {
|
||||
var params = this.params;
|
||||
var name = params.name;
|
||||
var tag = params.version;
|
||||
// normal: {name: $name, version: $version}
|
||||
// scope: [$name, $version]
|
||||
var orginalName = params.name || params[0];
|
||||
var name = orginalName;
|
||||
var tag = params.version || params[1];
|
||||
debug('display %s with %j', name, params);
|
||||
|
||||
var getPackageMethod;
|
||||
var getPackageArgs;
|
||||
@@ -44,31 +53,73 @@ exports.display = function *(next) {
|
||||
getPackageMethod = 'getByTag';
|
||||
getPackageArgs = [name, tag || 'latest'];
|
||||
}
|
||||
var r = yield [
|
||||
Module[getPackageMethod].apply(Module, getPackageArgs),
|
||||
down.total(name),
|
||||
ModuleDeps.list(name)
|
||||
];
|
||||
var pkg = r[0];
|
||||
var download = r[1];
|
||||
var dependents = r[2];
|
||||
|
||||
var pkg = yield Module[getPackageMethod].apply(Module, getPackageArgs);
|
||||
if (!pkg) {
|
||||
var adaptName = yield* Module.getAdaptName(name);
|
||||
if (adaptName) {
|
||||
name = adaptName;
|
||||
pkg = yield Module[getPackageMethod].apply(Module, [name, getPackageArgs[1]]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pkg || !pkg.package) {
|
||||
return yield next;
|
||||
// check if unpublished
|
||||
var unpublishedInfo = yield* ModuleUnpublished.get(name);
|
||||
debug('show unpublished %j', unpublishedInfo);
|
||||
if (unpublishedInfo) {
|
||||
var data = {
|
||||
name: name,
|
||||
unpublished: unpublishedInfo.package
|
||||
};
|
||||
data.unpublished.time = new Date(data.unpublished.time);
|
||||
if (data.unpublished.maintainers) {
|
||||
for (var i = 0; i < data.unpublished.maintainers.length; i++) {
|
||||
var maintainer = data.unpublished.maintainers[i];
|
||||
if (maintainer.email) {
|
||||
maintainer.gravatar = gravatar.url(maintainer.email, {s: '50', d: 'retro'}, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
yield this.render('package_unpublished', {
|
||||
package: data
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
return yield* next;
|
||||
}
|
||||
|
||||
var r = yield [
|
||||
down.total(name),
|
||||
ModuleDeps.list(name),
|
||||
ModuleStar.listUsers(name),
|
||||
packageService.listMaintainers(name)
|
||||
];
|
||||
var download = r[0];
|
||||
var dependents = (r[1] || []).map(function (item) {
|
||||
return item.deps;
|
||||
});
|
||||
var users = r[2];
|
||||
var maintainers = r[3];
|
||||
|
||||
pkg.package.fromNow = moment(pkg.publish_time).fromNow();
|
||||
pkg = pkg.package;
|
||||
pkg.users = users;
|
||||
pkg.readme = marked(pkg.readme || '');
|
||||
if (!pkg.readme) {
|
||||
pkg.readme = pkg.description || '';
|
||||
}
|
||||
|
||||
if (maintainers.length > 0) {
|
||||
pkg.maintainers = maintainers;
|
||||
}
|
||||
|
||||
if (pkg.maintainers) {
|
||||
for (var i = 0; i < pkg.maintainers.length; i++) {
|
||||
var maintainer = pkg.maintainers[i];
|
||||
if (maintainer.email) {
|
||||
maintainer.gravatar = gravatar.url(maintainer.email, {s: '50', d: 'retro'}, false);
|
||||
maintainer.gravatar = gravatar.url(maintainer.email, {s: '50', d: 'retro'}, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,7 +132,7 @@ exports.display = function *(next) {
|
||||
for (var i = 0; i < pkg.contributors.length; i++) {
|
||||
var contributor = pkg.contributors[i];
|
||||
if (contributor.email) {
|
||||
contributor.gravatar = gravatar.url(contributor.email, {s: '50', d: 'retro'}, false);
|
||||
contributor.gravatar = gravatar.url(contributor.email, {s: '50', d: 'retro'}, true);
|
||||
}
|
||||
if (config.packagePageContributorSearch || !contributor.url) {
|
||||
contributor.url = '/~' + encodeURIComponent(contributor.name);
|
||||
@@ -102,6 +153,14 @@ exports.display = function *(next) {
|
||||
|
||||
pkg.dependents = dependents;
|
||||
|
||||
if (pkg.dist) {
|
||||
pkg.dist.size = bytes(pkg.dist.size || 0);
|
||||
}
|
||||
|
||||
if (pkg.name !== orginalName) {
|
||||
pkg.name = orginalName;
|
||||
}
|
||||
|
||||
yield this.render('package', {
|
||||
title: 'Package - ' + pkg.name,
|
||||
package: pkg,
|
||||
@@ -111,22 +170,34 @@ exports.display = function *(next) {
|
||||
|
||||
exports.search = function *(next) {
|
||||
var params = this.params;
|
||||
var word = params.word;
|
||||
var word = params.word || params[0];
|
||||
debug('search %j', word);
|
||||
var result = yield Module.search(word);
|
||||
|
||||
var match = null;
|
||||
for (var i = 0; i < result.searchMatchs.length; i++) {
|
||||
var p = result.searchMatchs[i];
|
||||
if (p.name === word) {
|
||||
match = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// return a json result
|
||||
if (this.query && this.query.type === 'json') {
|
||||
this.body = {
|
||||
keyword: word,
|
||||
match: match,
|
||||
packages: result.searchMatchs,
|
||||
keywords: result.keywordMatchs
|
||||
keywords: result.keywordMatchs,
|
||||
};
|
||||
this.charset = 'utf-8';
|
||||
this.type = 'application/json; charset=utf-8';
|
||||
return;
|
||||
}
|
||||
|
||||
yield this.render('search', {
|
||||
title: 'Keyword - ' + word,
|
||||
keyword: word,
|
||||
match: match,
|
||||
packages: result.searchMatchs,
|
||||
keywords: result.keywordMatchs,
|
||||
});
|
||||
@@ -163,14 +234,22 @@ exports.rangeSearch = function *(next) {
|
||||
};
|
||||
};
|
||||
|
||||
exports.displaySync = function *(next) {
|
||||
var name = this.params.name || this.query.name;
|
||||
exports.displaySync = function* (next) {
|
||||
var name = this.params.name || this.params[0] || this.query.name;
|
||||
yield this.render('sync', {
|
||||
name: name,
|
||||
title: 'Sync - ' + name
|
||||
});
|
||||
};
|
||||
|
||||
exports.listPrivates = function* () {
|
||||
var packages = yield Module.listPrivates();
|
||||
yield this.render('private', {
|
||||
title: 'private packages',
|
||||
packages: packages
|
||||
});
|
||||
};
|
||||
|
||||
function setLicense(pkg) {
|
||||
var license;
|
||||
license = pkg.license || pkg.licenses || pkg.licence || pkg.licences;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - controllers/web/package.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -6,6 +6,7 @@
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -13,26 +14,68 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var config = require('../../config');
|
||||
var Module = require('../../proxy/module');
|
||||
var User = require('../../proxy/user');
|
||||
var UserService = require('../../services/user');
|
||||
var common = require('../../lib/common');
|
||||
|
||||
exports.display = function *(next) {
|
||||
exports.display = function* (next) {
|
||||
var name = this.params.name;
|
||||
var isAdmin = common.isAdmin(name);
|
||||
var scopes = config.scopes || [];
|
||||
if (config.customUserService) {
|
||||
var customUser = yield* UserService.get(name);
|
||||
if (customUser) {
|
||||
isAdmin = !!customUser.site_admin;
|
||||
scopes = customUser.scopes;
|
||||
var data = {
|
||||
user: customUser
|
||||
};
|
||||
yield* User.saveCustomUser(data);
|
||||
}
|
||||
}
|
||||
|
||||
var r = yield [Module.listByAuthor(name), User.get(name)];
|
||||
var packages = r[0];
|
||||
var packages = r[0] || [];
|
||||
var user = r[1];
|
||||
if (!user && !packages.length) {
|
||||
return yield next;
|
||||
return yield* next;
|
||||
}
|
||||
user = {
|
||||
|
||||
user = user || {};
|
||||
|
||||
var data = {
|
||||
name: name,
|
||||
email: user && user.email
|
||||
email: user.email,
|
||||
json: user.json || {}
|
||||
};
|
||||
|
||||
if (data.json.login) {
|
||||
// custom user format
|
||||
// convert to npm user format
|
||||
var json = data.json;
|
||||
data.json = {
|
||||
_id: 'org.couchdb.user:' + user.name,
|
||||
_rev: user.rev,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
type: 'user',
|
||||
roles: [],
|
||||
date: user.gmt_modified,
|
||||
avatar: json.avatar_url,
|
||||
fullname: json.name || json.login,
|
||||
homepage: json.html_url,
|
||||
};
|
||||
}
|
||||
|
||||
yield this.render('profile', {
|
||||
title: 'User - ' + name,
|
||||
packages: packages || [],
|
||||
user: user
|
||||
packages: packages,
|
||||
user: data,
|
||||
lastModified: user && user.gmt_modified,
|
||||
isAdmin: isAdmin,
|
||||
scopes: scopes
|
||||
});
|
||||
};
|
||||
|
||||
68
dispatch.js
68
dispatch.js
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - dispatch.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -17,43 +17,51 @@
|
||||
|
||||
var path = require('path');
|
||||
var util = require('util');
|
||||
var fs = require('fs');
|
||||
var cluster = require('cluster');
|
||||
var cfork = require('cfork');
|
||||
var config = require('./config');
|
||||
var workerPath = path.join(__dirname, 'worker.js');
|
||||
var childProcess = require('child_process');
|
||||
var syncPath = path.join(__dirname, 'sync');
|
||||
|
||||
if (config.enableCluster) {
|
||||
cluster.setupMaster({
|
||||
exec: workerPath
|
||||
});
|
||||
|
||||
cluster.on('fork', function (worker) {
|
||||
console.log('[%s] [worker:%d] new worker start', new Date(), worker.process.pid);
|
||||
});
|
||||
|
||||
cluster.on('disconnect', function (worker) {
|
||||
var w = cluster.fork();
|
||||
console.error('[%s] [master:%s] wroker:%s disconnect! new worker:%s fork',
|
||||
new Date(), process.pid, worker.process.pid, w.process.pid);
|
||||
});
|
||||
|
||||
cluster.on('exit', function (worker, code, signal) {
|
||||
var exitCode = worker.process.exitCode;
|
||||
var err = new Error(util.format('worker %s died (code: %s, signal: %s)', worker.process.pid, exitCode, signal));
|
||||
err.name = 'WorkerDiedError';
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
var numCPUs = require('os').cpus().length;
|
||||
// Fork workers.
|
||||
for (var i = 0; i < numCPUs; i++) {
|
||||
cluster.fork();
|
||||
forkWorker();
|
||||
if (config.syncModel !== 'none') {
|
||||
forkSyncer();
|
||||
}
|
||||
|
||||
childProcess.fork(syncPath);
|
||||
} else {
|
||||
require(workerPath);
|
||||
require(syncPath);
|
||||
if (config.syncModel !== 'none') {
|
||||
require(syncPath);
|
||||
}
|
||||
}
|
||||
|
||||
function forkWorker() {
|
||||
cfork({
|
||||
exec: workerPath,
|
||||
count: config.numCPUs,
|
||||
}).on('fork', function (worker) {
|
||||
console.log('[%s] [worker:%d] new worker start', Date(), worker.process.pid);
|
||||
}).on('disconnect', function (worker) {
|
||||
console.error('[%s] [master:%s] wroker:%s disconnect, suicide: %s, state: %s.',
|
||||
Date(), process.pid, worker.process.pid, worker.suicide, worker.state);
|
||||
}).on('exit', function (worker, code, signal) {
|
||||
var exitCode = worker.process.exitCode;
|
||||
var err = new Error(util.format('worker %s died (code: %s, signal: %s, suicide: %s, state: %s)',
|
||||
worker.process.pid, exitCode, signal, worker.suicide, worker.state));
|
||||
err.name = 'WorkerDiedError';
|
||||
console.error('[%s] [master:%s] wroker exit: %s', Date(), process.pid, err.stack);
|
||||
});
|
||||
}
|
||||
|
||||
function forkSyncer() {
|
||||
var syncer = childProcess.fork(syncPath);
|
||||
syncer.on('exit', function (code, signal) {
|
||||
var err = new Error(util.format('syncer %s died (code: %s, signal: %s, stdout: %s, stderr: %s)',
|
||||
syncer.pid, code, signal, syncer.stdout, syncer.stderr));
|
||||
err.name = 'SyncerWorkerDiedError';
|
||||
console.error('[%s] [master:%s] syncer exit: %s: %s',
|
||||
Date(), process.pid, err.name, err.message);
|
||||
setTimeout(forkSyncer, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
67
docs/db.sql
67
docs/db.sql
@@ -9,10 +9,15 @@ CREATE TABLE `user` (
|
||||
`roles` varchar(200) NOT NULL DEFAULT '[]',
|
||||
`rev` varchar(40) NOT NULL,
|
||||
`email` varchar(400) NOT NULL,
|
||||
`json` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'json details',
|
||||
`npm_user` tinyint(1) DEFAULT '0' COMMENT 'user sync from npm or not, 1: true, other: false',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`name`),
|
||||
KEY `gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='user base info';
|
||||
-- ALTER TABLE `user`
|
||||
-- ADD `json` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'json details',
|
||||
-- ADD `npm_user` tinyint(1) DEFAULT '0' COMMENT 'user sync from npm or not, 1: true, other: false';
|
||||
|
||||
CREATE TABLE `module_keyword` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
@@ -25,6 +30,26 @@ CREATE TABLE `module_keyword` (
|
||||
KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module keyword';
|
||||
|
||||
CREATE TABLE `module_star` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `user_module_name` (`user`,`name`),
|
||||
KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module star';
|
||||
|
||||
CREATE TABLE `module_maintainer` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `user_module_name` (`user`,`name`),
|
||||
KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module maintainers';
|
||||
|
||||
CREATE TABLE `module` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
@@ -72,10 +97,23 @@ CREATE TABLE `tag` (
|
||||
`version` varchar(30) NOT NULL COMMENT 'module version',
|
||||
`module_id` bigint(20) unsigned NOT NULL COMMENT 'module id',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`name`, `tag`)
|
||||
UNIQUE KEY `name` (`name`, `tag`),
|
||||
KEY `gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module tag';
|
||||
-- ALTER TABLE `tag` ADD `module_id` BIGINT( 20 ) UNSIGNED NOT NULL;
|
||||
-- ALTER TABLE `tag` CHANGE `name` `name` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
|
||||
-- ALTER TABLE `tag` ADD KEY `gmt_modified` (`gmt_modified`);
|
||||
|
||||
CREATE TABLE `module_unpublished` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`package` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'base info: tags, time, maintainers, description, versions',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`name`),
|
||||
KEY `gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module unpublished info';
|
||||
|
||||
CREATE TABLE `total` (
|
||||
`name` varchar(100) NOT NULL COMMENT 'total name',
|
||||
@@ -122,3 +160,30 @@ CREATE TABLE `module_deps` (
|
||||
UNIQUE KEY `name_deps` (`name`,`deps`),
|
||||
KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module deps';
|
||||
|
||||
CREATE TABLE `dist_dir` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(200) NOT NULL COMMENT 'user name',
|
||||
`parent` varchar(200) NOT NULL COMMENT 'parent dir' DEFAULT '/',
|
||||
`date` varchar(20) COMMENT '02-May-2014 01:06',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`parent`, `name`),
|
||||
KEY `gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='dist dir info';
|
||||
|
||||
CREATE TABLE `dist_file` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(100) NOT NULL COMMENT 'user name',
|
||||
`parent` varchar(200) NOT NULL COMMENT 'parent dir' DEFAULT '/',
|
||||
`date` varchar(20) COMMENT '02-May-2014 01:06',
|
||||
`size` int(10) unsigned NOT NULL COMMENT 'file size' DEFAULT '0',
|
||||
`sha1` varchar(40) COMMENT 'sha1 hex value',
|
||||
`url` varchar(2048),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `fullname` (`parent`, `name`),
|
||||
KEY `gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='dist file info';
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# npm publish flow
|
||||
|
||||
## Flows
|
||||
|
||||
1. try to put package.json and tgz, maybe base64 tgz body
|
||||
2. if new version not exists, publish success
|
||||
3. if new version exists, 409, try to get full package info with ?write=true
|
||||
4. if new version had publish, show: "Update the 'version' field in package.json and try again."
|
||||
|
||||
```bash
|
||||
$ cnpm publish
|
||||
npm http PUT http://r.cnpmjs.org/cnpmjs.org
|
||||
npm http 409 http://r.cnpmjs.org/cnpmjs.org
|
||||
npm http GET http://r.cnpmjs.org/cnpmjs.org?write=true
|
||||
npm http 200 http://r.cnpmjs.org/cnpmjs.org?write=true
|
||||
npm ERR! publish fail Cannot publish over existing version.
|
||||
npm ERR! publish fail Update the 'version' field in package.json and try again.
|
||||
```
|
||||
|
||||
## Details
|
||||
|
||||
code: https://github.com/isaacs/npm-registry-client/blob/master/lib/publish.js
|
||||
|
||||
* couch login if token not exists: [couch-login](https://github.com/isaacs/couch-login)
|
||||
9
docs/new.sql
Normal file
9
docs/new.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
CREATE TABLE `module_maintainer` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `user_module_name` (`user`,`name`),
|
||||
KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module maintainers';
|
||||
833
docs/registry-api.md
Normal file
833
docs/registry-api.md
Normal file
@@ -0,0 +1,833 @@
|
||||
# NPM Registry API
|
||||
|
||||
## Overview
|
||||
|
||||
* [Schema](/docs/registry-api.md#schema)
|
||||
* [Client Errors](/docs/registry-api.md#client-errors)
|
||||
* [Authentication](/docs/registry-api.md#authentication)
|
||||
* [Package](/docs/registry-api.md#package)
|
||||
* [User](/docs/registry-api.md#user)
|
||||
* [Search](/docs/registry-api.md#search)
|
||||
|
||||
## Schema
|
||||
|
||||
All API access is over HTTPS or HTTP,
|
||||
and accessed from the `registry.npmjs.org` domain.
|
||||
All data is sent and received as JSON.
|
||||
|
||||
```bash
|
||||
$ curl -i https://registry.npmjs.org
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Date: Tue, 05 Aug 2014 10:53:24 GMT
|
||||
Server: CouchDB/1.5.0 (Erlang OTP/R16B03)
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Cache-Control: max-age=60
|
||||
Content-Length: 258
|
||||
Accept-Ranges: bytes
|
||||
Via: 1.1 varnish
|
||||
Age: 11
|
||||
X-Served-By: cache-ty67-TYO
|
||||
X-Cache: HIT
|
||||
X-Cache-Hits: 1
|
||||
X-Timer: S1407236004.867906,VS0,VE0
|
||||
|
||||
{"db_name":"registry","doc_count":90789,"doc_del_count":381,"update_seq":137250,"purge_seq":0,
|
||||
"compact_running":false,"disk_size":436228219,"data_size":332875061,
|
||||
"instance_start_time":"1405721973718703","disk_format_version":6,"committed_update_seq":137250}
|
||||
```
|
||||
|
||||
## Client Errors
|
||||
|
||||
```json
|
||||
Status: 4xx
|
||||
|
||||
{
|
||||
"error": "error_name",
|
||||
"reason": "error reason string"
|
||||
}
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
There is only one way to authenticate through the API.
|
||||
|
||||
## Basic Authentication
|
||||
|
||||
```bash
|
||||
$ curl -u "username:password" https://registry.npmjs.org
|
||||
```
|
||||
|
||||
## Failed login limit
|
||||
|
||||
```bash
|
||||
$ curl -i -X PUT -u foo:pwd \
|
||||
-d '{"name":"foo","email":"foo@bar.com","type":"user","roles":[]}' \
|
||||
https://registry.npmjs.org/-/user/org.couchdb.user:foo/-rev/11-d226c6afa9286ab5b9eb858c429bdabf
|
||||
|
||||
HTTP/1.1 401 Unauthorized
|
||||
Date: Tue, 05 Aug 2014 15:33:25 GMT
|
||||
Server: CouchDB/1.5.0 (Erlang OTP/R14B04)
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Cache-Control: max-age=60
|
||||
Content-Length: 67
|
||||
Accept-Ranges: bytes
|
||||
Via: 1.1 varnish
|
||||
X-Served-By: cache-ty66-TYO
|
||||
X-Cache: MISS
|
||||
X-Cache-Hits: 0
|
||||
X-Timer: S1407252805.261390,VS0,VE434
|
||||
|
||||
{"error":"unauthorized","reason":"Name or password is incorrect."}
|
||||
```
|
||||
|
||||
## Package
|
||||
|
||||
* Read
|
||||
* [Get a single package](/docs/registry-api.md#get-a-single-package)
|
||||
* [Get a special version or tag package](/docs/registry-api.md#get-a-special-version-or-tag-package)
|
||||
* [List packages since from a update time](/docs/registry-api.md#list-packages-since-from-a-update-time)
|
||||
* Write
|
||||
* [Publish a new package](/docs/registry-api.md#publish-a-new-package)
|
||||
* [Update a package's tag](/docs/registry-api.md#update-a-packages-tag)
|
||||
* [Update a package's maintainers](/docs/registry-api.md#update-a-packages-maintainers)
|
||||
* [Remove one version from package](/docs/registry-api.md#remove-one-version-from-package)
|
||||
* [Remove a tgz file from package](/docs/registry-api.md#remove-a-tgz-file-from-package)
|
||||
|
||||
### Get a single package
|
||||
|
||||
```
|
||||
GET /:package
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
```json
|
||||
HTTP/1.1 200 OK
|
||||
Etag: "8UDCP753LFXOG42NMX88JAN40"
|
||||
Content-Type: application/json
|
||||
Cache-Control: max-age=60
|
||||
Content-Length: 2243
|
||||
|
||||
{
|
||||
"_id": "pedding",
|
||||
"_rev": "11-e6d1e6e96eaf72433fef6aaabe843af8",
|
||||
"name": "pedding",
|
||||
"description": "Just pedding for callback.",
|
||||
"dist-tags": {
|
||||
"latest": "1.0.0"
|
||||
},
|
||||
"versions": {
|
||||
"1.0.0": {
|
||||
"name": "pedding",
|
||||
"version": "1.0.0",
|
||||
"description": "Just pedding for callback.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "make test-all"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/fengmk2/pedding.git"
|
||||
},
|
||||
"keywords": [
|
||||
"pedding",
|
||||
"callback"
|
||||
],
|
||||
"devDependencies": {
|
||||
"contributors": "*",
|
||||
"mocha": "*",
|
||||
"mocha-phantomjs": "*",
|
||||
"component": "*",
|
||||
"chai": "*"
|
||||
},
|
||||
"author": {
|
||||
"name": "fengmk2",
|
||||
"email": "fengmk2@gmail.com"
|
||||
},
|
||||
"license": "MIT",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "fengmk2",
|
||||
"email": "fengmk2@gmail.com",
|
||||
"url": "https://github.com/fengmk2"
|
||||
},
|
||||
{
|
||||
"name": "dead-horse",
|
||||
"email": "dead_horse@qq.com",
|
||||
"url": "https://github.com/dead-horse"
|
||||
}
|
||||
],
|
||||
"gitHead": "b42a708414a704336e9dee570a963e2dbe43e529",
|
||||
"bugs": {
|
||||
"url": "https://github.com/fengmk2/pedding/issues"
|
||||
},
|
||||
"homepage": "https://github.com/fengmk2/pedding",
|
||||
"_id": "pedding@1.0.0",
|
||||
"_shasum": "7f5098d60307b4ef7240c3d693cb20a9473c6074",
|
||||
"_from": ".",
|
||||
"_npmVersion": "1.4.13",
|
||||
"_npmUser": {
|
||||
"name": "fengmk2",
|
||||
"email": "fengmk2@gmail.com"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "fengmk2",
|
||||
"email": "fengmk2@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "dead-horse",
|
||||
"email": "dead_horse@qq.com"
|
||||
}
|
||||
],
|
||||
"dist": {
|
||||
"shasum": "7f5098d60307b4ef7240c3d693cb20a9473c6074",
|
||||
"tarball": "http://registry.npmjs.org/pedding/-/pedding-1.0.0.tgz"
|
||||
},
|
||||
"directories": {}
|
||||
}
|
||||
},
|
||||
"readme": "# pedding\n readme...",
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "fengmk2",
|
||||
"email": "fengmk2@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "dead-horse",
|
||||
"email": "dead_horse@qq.com"
|
||||
},
|
||||
{
|
||||
"name": "dead_horse",
|
||||
"email": "dead_horse@qq.com"
|
||||
}
|
||||
],
|
||||
"time": {
|
||||
"modified": "2014-07-05T14:22:53.849Z",
|
||||
"created": "2012-09-18T14:46:08.346Z",
|
||||
"0.0.1": "2012-09-18T14:46:21.321Z",
|
||||
"0.0.2": "2013-06-22T08:26:45.125Z",
|
||||
"0.0.3": "2013-07-02T15:20:34.707Z",
|
||||
"1.0.0": "2014-07-05T11:08:51.614Z"
|
||||
},
|
||||
"author": {
|
||||
"name": "fengmk2",
|
||||
"email": "fengmk2@gmail.com"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/fengmk2/pedding.git"
|
||||
},
|
||||
"keywords": [
|
||||
"pedding",
|
||||
"callback"
|
||||
],
|
||||
"bugs": {
|
||||
"url": "https://github.com/fengmk2/pedding/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"readmeFilename": "README.md",
|
||||
"homepage": "https://github.com/fengmk2/pedding",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "fengmk2",
|
||||
"email": "fengmk2@gmail.com",
|
||||
"url": "https://github.com/fengmk2"
|
||||
},
|
||||
{
|
||||
"name": "dead-horse",
|
||||
"email": "dead_horse@qq.com",
|
||||
"url": "https://github.com/dead-horse"
|
||||
}
|
||||
],
|
||||
"_attachments": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Get a special version or tag package
|
||||
|
||||
```
|
||||
GET /:package/:tag_or_version
|
||||
```
|
||||
|
||||
#### Reponse
|
||||
|
||||
```json
|
||||
HTTP/1.1 200 OK
|
||||
Etag: "1WJ4JF535RO3BDZR2BARXSGLY"
|
||||
Content-Type: application/json
|
||||
Cache-Control: max-age=60
|
||||
Content-Length: 1183
|
||||
|
||||
{
|
||||
"name": "pedding",
|
||||
"version": "1.0.0",
|
||||
"description": "Just pedding for callback.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "make test-all"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/fengmk2/pedding.git"
|
||||
},
|
||||
"keywords": [
|
||||
"pedding",
|
||||
"callback"
|
||||
],
|
||||
"devDependencies": {
|
||||
"contributors": "*",
|
||||
"mocha": "*",
|
||||
"mocha-phantomjs": "*",
|
||||
"component": "*",
|
||||
"chai": "*"
|
||||
},
|
||||
"author": {
|
||||
"name": "fengmk2",
|
||||
"email": "fengmk2@gmail.com"
|
||||
},
|
||||
"license": "MIT",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "fengmk2",
|
||||
"email": "fengmk2@gmail.com",
|
||||
"url": "https://github.com/fengmk2"
|
||||
},
|
||||
{
|
||||
"name": "dead-horse",
|
||||
"email": "dead_horse@qq.com",
|
||||
"url": "https://github.com/dead-horse"
|
||||
}
|
||||
],
|
||||
"gitHead": "b42a708414a704336e9dee570a963e2dbe43e529",
|
||||
"bugs": {
|
||||
"url": "https://github.com/fengmk2/pedding/issues"
|
||||
},
|
||||
"homepage": "https://github.com/fengmk2/pedding",
|
||||
"_id": "pedding@1.0.0",
|
||||
"_shasum": "7f5098d60307b4ef7240c3d693cb20a9473c6074",
|
||||
"_from": ".",
|
||||
"_npmVersion": "1.4.13",
|
||||
"_npmUser": {
|
||||
"name": "fengmk2",
|
||||
"email": "fengmk2@gmail.com"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "fengmk2",
|
||||
"email": "fengmk2@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "dead-horse",
|
||||
"email": "dead_horse@qq.com"
|
||||
}
|
||||
],
|
||||
"dist": {
|
||||
"shasum": "7f5098d60307b4ef7240c3d693cb20a9473c6074",
|
||||
"tarball": "http://registry.npmjs.org/pedding/-/pedding-1.0.0.tgz"
|
||||
},
|
||||
"directories": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Publish a new package
|
||||
|
||||
* Authentication required.
|
||||
|
||||
```
|
||||
PUT /:package
|
||||
```
|
||||
|
||||
#### Input
|
||||
|
||||
```json
|
||||
{
|
||||
"_id": "pedding",
|
||||
"name": "pedding",
|
||||
"description": "Just pedding for callback.",
|
||||
"dist-tags": {
|
||||
"latest": "1.0.0"
|
||||
},
|
||||
"versions": {
|
||||
"1.0.0": {
|
||||
"name": "pedding",
|
||||
"version": "1.0.0",
|
||||
"description": "Just pedding for callback.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "make test-all"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/fengmk2/pedding.git"
|
||||
},
|
||||
"keywords": [ "pedding","callback" ],
|
||||
"devDependencies": {
|
||||
"contributors": "*",
|
||||
"mocha": "*",
|
||||
"mocha-phantomjs": "*",
|
||||
"component": "*",
|
||||
"chai": "*"
|
||||
},
|
||||
"dependencies": {},
|
||||
"author": {
|
||||
"name": "fengmk2",
|
||||
"email": "fengmk2@gmail.com"
|
||||
},
|
||||
"license": "MIT",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "fengmk2",
|
||||
"email": "fengmk2@gmail.com",
|
||||
"url": "https://github.com/fengmk2"
|
||||
},
|
||||
{
|
||||
"name": "dead-horse",
|
||||
"email": "dead_horse@qq.com",
|
||||
"url": "https://github.com/dead-horse"
|
||||
}
|
||||
],
|
||||
"readme": "# pedding ...",
|
||||
"readmeFilename": "README.md",
|
||||
"gitHead": "b42a708414a704336e9dee570a963e2dbe43e529",
|
||||
"bugs": {
|
||||
"url": "https://github.com/fengmk2/pedding/issues"
|
||||
},
|
||||
"homepage": "https://github.com/fengmk2/pedding",
|
||||
"_id": "pedding@1.0.0",
|
||||
"_shasum": "7f5098d60307b4ef7240c3d693cb20a9473c6074",
|
||||
"_from": ".",
|
||||
"_npmVersion": "1.5.0-alpha-4",
|
||||
"_npmUser": {
|
||||
"name": "admin",
|
||||
"email": "fengmk2@gmail.com"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "admin",
|
||||
"email": "fengmk2@gmail.com"
|
||||
}
|
||||
],
|
||||
"dist": {
|
||||
"shasum": "7f5098d60307b4ef7240c3d693cb20a9473c6074",
|
||||
"tarball": "https://registry.npmjs.org/pedding/-/pedding-1.0.0.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"readme": "# pedding ...",
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "admin",
|
||||
"email": "fengmk2@gmail.com"
|
||||
}
|
||||
],
|
||||
"_attachments": {
|
||||
"pedding-1.0.0.tgz":{
|
||||
"content_type": "application/octet-stream",
|
||||
"data": "H4sIAAAAAAAAA+0aa3PbNjKf8Su...",
|
||||
"length": 2107
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
```json
|
||||
Status: 201 Created
|
||||
|
||||
{
|
||||
"ok": true,
|
||||
"rev": "11-e6d1e6e96eaf72433fef6aaabe843af8"
|
||||
}
|
||||
```
|
||||
|
||||
### Update a package's tag
|
||||
|
||||
* Authentication required.
|
||||
|
||||
```
|
||||
PUT /:package/:tag
|
||||
```
|
||||
|
||||
#### Input
|
||||
|
||||
The total input body is the `version` string which's setting to the tag.
|
||||
|
||||
```json
|
||||
"1.0.0"
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
```json
|
||||
Status: 201 Created
|
||||
|
||||
{
|
||||
"ok": true
|
||||
}
|
||||
```
|
||||
|
||||
### Update a package's maintainers
|
||||
|
||||
* Authentication required.
|
||||
|
||||
```
|
||||
PUT /:package/-rev/:rev
|
||||
```
|
||||
|
||||
#### Input
|
||||
|
||||
```json
|
||||
{
|
||||
"_id": "pedding",
|
||||
"_rev": "11-e6d1e6e96eaf72433fef6aaabe843af8",
|
||||
"maintainers":[
|
||||
{ "name": "fengmk2", "email": "fengmk2@gmail.com" },
|
||||
{ "name": "dead-horse", "email": "dead_horse@qq.com" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
```json
|
||||
Status: 201 Created
|
||||
|
||||
{
|
||||
"ok": true,
|
||||
"id": "pedding",
|
||||
"rev": "12-bb300a90c9aeb779748b83ec1b744039"
|
||||
}
|
||||
```
|
||||
|
||||
### Remove one version from package
|
||||
|
||||
* Authentication required.
|
||||
|
||||
```
|
||||
PUT /:package/-rev/:rev
|
||||
```
|
||||
|
||||
#### Input
|
||||
|
||||
Example for removing `0.0.1` version:
|
||||
|
||||
```json
|
||||
{
|
||||
"_id": "pedding",
|
||||
"_rev": "12-bb300a90c9aeb779748b83ec1b744039",
|
||||
"name": "pedding",
|
||||
"description": "desc",
|
||||
"dist-tags": { "latest": "1.0.0" },
|
||||
"maintainers":
|
||||
[ ... ],
|
||||
"time":
|
||||
{ ... },
|
||||
"users": {},
|
||||
"author": { ... },
|
||||
"repository": { ... },
|
||||
"versions":
|
||||
{ "1.0.0":
|
||||
{ ... },
|
||||
"0.0.3":
|
||||
{ ... },
|
||||
"0.0.2":
|
||||
{ ... } },
|
||||
"readme": "...",
|
||||
"homepage": "https://github.com/fengmk2/pedding",
|
||||
"bugs": { ... },
|
||||
"license": "MIT" }
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
```json
|
||||
Status: 201 Created
|
||||
|
||||
{
|
||||
"ok": true
|
||||
}
|
||||
```
|
||||
|
||||
### Remove a tgz file from package
|
||||
|
||||
* Authentication required.
|
||||
|
||||
```
|
||||
DELETE /:tgzfilepath/-rev/:rev
|
||||
```
|
||||
|
||||
Exmaple for removing `https://registry.npmjs.org/pedding/-/pedding-0.0.1.tgz` file:
|
||||
|
||||
```
|
||||
DELETE /pedding/-/pedding-0.0.1.tgz/-rev/12-bb300a90c9aeb779748b83ec1b744039
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
```json
|
||||
Status: 200 OK
|
||||
|
||||
{
|
||||
"ok": true
|
||||
}
|
||||
```
|
||||
|
||||
### List packages since from a update time
|
||||
|
||||
```
|
||||
GET /-/all/since?stale=update_after&startkey=:startkey
|
||||
```
|
||||
|
||||
* `startkey` is a ms timestamp
|
||||
|
||||
#### Response
|
||||
|
||||
```bash
|
||||
$ curl -i "https://registry.npmjs.org/-/all/since?stale=update_after&startkey=1407255748643"
|
||||
```
|
||||
|
||||
```json
|
||||
HTTP/1.1 200 OK
|
||||
|
||||
{
|
||||
"_updated": 1407255883282,
|
||||
"bacon-and-eggs": {
|
||||
"name": "bacon-and-eggs",
|
||||
"description": "A functional reactive Twitter API client in node",
|
||||
"dist-tags": {
|
||||
"latest": "0.0.4"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "mikegroseclose",
|
||||
"email": "mike.groseclose@gmail.com"
|
||||
}
|
||||
],
|
||||
"homepage": "http://github.com/mikegroseclose/bacon-and-eggs",
|
||||
"keywords": [
|
||||
"twitter",
|
||||
"api",
|
||||
"frp",
|
||||
"functional",
|
||||
"reactive",
|
||||
"bacon",
|
||||
"eggs",
|
||||
"oauth",
|
||||
"stream",
|
||||
"streams"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/mikegroseclose/gulp-regex-replace.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Mike Groseclose",
|
||||
"email": "mike.groseclose@gmail.com",
|
||||
"url": "http://mikegroseclose.com"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/mikegroseclose/gulp-regex-replace/issues"
|
||||
},
|
||||
"readmeFilename": "README.md",
|
||||
"time": {
|
||||
"modified": "2014-08-05T16:21:17.041Z"
|
||||
},
|
||||
"versions": {
|
||||
"0.0.4": "latest"
|
||||
}
|
||||
},
|
||||
"git-perm-rm": {
|
||||
"name": "git-perm-rm",
|
||||
"description": "Permanently remove a file or directory from a git repo including all related commit records.",
|
||||
"dist-tags": {
|
||||
"latest": "1.0.1"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "kael",
|
||||
"email": "i@kael.me"
|
||||
}
|
||||
],
|
||||
"homepage": "https://github.com/kaelzhang/git-perm-rm",
|
||||
"keywords": [
|
||||
"git",
|
||||
"rm",
|
||||
"git-perm-rm",
|
||||
"remove",
|
||||
"permanently"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/kaelzhang/git-perm-rm.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Kael"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/kaelzhang/git-perm-rm/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"readmeFilename": "README.md",
|
||||
"time": {
|
||||
"modified": "2014-08-05T16:22:41.253Z"
|
||||
},
|
||||
"versions": {
|
||||
"1.0.1": "latest"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## User
|
||||
|
||||
* [Get a single user](/docs/registry-api.md#get-a-single-user)
|
||||
* [Add a new user](/docs/registry-api.md#add-a-new-user)
|
||||
* [Update a exists user](/docs/registry-api.md#update-a-exists-user)
|
||||
|
||||
### Get a single user
|
||||
|
||||
```
|
||||
GET /-/user/org.couchdb.user::username
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
```json
|
||||
HTTP/1.1 200 OK
|
||||
ETag: "32-984ee97e01aea166dcab6d1517c730e3"
|
||||
|
||||
{
|
||||
"_id": "org.couchdb.user:fengmk2",
|
||||
"_rev": "32-984ee97e01aea166dcab6d1517c730e3",
|
||||
"name": "fengmk2",
|
||||
"email": "fengmk2@gmail.com",
|
||||
"type": "user",
|
||||
"roles": [],
|
||||
"date": "2014-08-04T10:43:07.063Z",
|
||||
"fullname": "fengmk2",
|
||||
"avatar": "https://secure.gravatar.com/avatar/95b9d41231617a05ced5604d242c9670?s=50&d=retro",
|
||||
"freenode": "",
|
||||
"github": "fengmk2",
|
||||
"homepage": "http://fengmk2.github.com",
|
||||
"twitter": "fengmk2",
|
||||
"avatarMedium": "https://secure.gravatar.com/avatar/95b9d41231617a05ced5604d242c9670?s=100&d=retro",
|
||||
"avatarLarge": "https://secure.gravatar.com/avatar/95b9d41231617a05ced5604d242c9670?s=496&d=retro",
|
||||
"fields": [
|
||||
{
|
||||
"name": "fullname",
|
||||
"value": "fengmk2",
|
||||
"title": "Full Name",
|
||||
"show": "fengmk2"
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"value": "fengmk2@gmail.com",
|
||||
"title": "Email",
|
||||
"show": "<a href=\"mailto:fengmk2@gmail.com\">fengmk2@gmail.com</a>"
|
||||
},
|
||||
{
|
||||
"name": "github",
|
||||
"value": "fengmk2",
|
||||
"title": "Github",
|
||||
"show": "<a rel=\"me\" href=\"https://github.com/fengmk2\">fengmk2</a>"
|
||||
},
|
||||
{
|
||||
"name": "twitter",
|
||||
"value": "fengmk2",
|
||||
"title": "Twitter",
|
||||
"show": "<a rel=\"me\" href=\"https://twitter.com/fengmk2\">@fengmk2</a>"
|
||||
},
|
||||
{
|
||||
"name": "appdotnet",
|
||||
"value": "",
|
||||
"title": "App.net",
|
||||
"show": ""
|
||||
},
|
||||
{
|
||||
"name": "homepage",
|
||||
"value": "http://fengmk2.github.com",
|
||||
"title": "Homepage",
|
||||
"show": "<a rel=\"me\" href=\"http://fengmk2.github.com/\">http://fengmk2.github.com</a>"
|
||||
},
|
||||
{
|
||||
"name": "freenode",
|
||||
"value": "",
|
||||
"title": "IRC Handle",
|
||||
"show": ""
|
||||
}
|
||||
],
|
||||
"appdotnet": "fengmk2"
|
||||
}
|
||||
```
|
||||
|
||||
### Add a new user
|
||||
|
||||
```
|
||||
PUT /-/user/org.couchdb.user::username
|
||||
```
|
||||
|
||||
#### Input
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "admin",
|
||||
"password": "123",
|
||||
"email": "fengmk2@gmail.com",
|
||||
"_id": "org.couchdb.user:admin",
|
||||
"type": "user",
|
||||
"roles": [],
|
||||
"date": "2014-08-05T16:05:17.792Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
```json
|
||||
Status: 201 Created
|
||||
|
||||
{
|
||||
"ok": true,
|
||||
"id": "org.couchdb.user:fengmk2",
|
||||
"rev": "32-984ee97e01aea166dcab6d1517c730e3"
|
||||
}
|
||||
```
|
||||
|
||||
### Update a exists user
|
||||
|
||||
* Authentication required.
|
||||
|
||||
```
|
||||
PUT /-/user/org.couchdb.user::username/-rev/:rev
|
||||
```
|
||||
|
||||
#### Input
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "admin",
|
||||
"password": "123",
|
||||
"email": "fengmk2@gmail.com",
|
||||
"_id": "org.couchdb.user:admin",
|
||||
"type": "user",
|
||||
"roles": [],
|
||||
"date": "2014-08-05T16:05:17.792Z",
|
||||
"_rev": "2-1a18c3d73ba42e863523a399ff3304d8"
|
||||
}
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
```json
|
||||
Status: 201 Created
|
||||
|
||||
{
|
||||
"ok": true,
|
||||
"id": "org.couchdb.user:fengmk2",
|
||||
"rev": "3-bb300a90c9aeb779748b83ec1b744039"
|
||||
}
|
||||
```
|
||||
|
||||
## Search
|
||||
@@ -103,13 +103,13 @@ npm install -g cnpm
|
||||
alias lnpm='cnpm --registry=http://localhost:7001\
|
||||
--registryweb=http://localhost:7002\
|
||||
--cache=$HOME/.npm/.cache/lnpm\
|
||||
--disturl=http://dist.u.qiniudn.com\
|
||||
--disturl=http://cnpmjs.org/dist\
|
||||
--userconfig=$HOME/.lnpmrc'
|
||||
|
||||
#or put this in .zshrc or .bashrc
|
||||
echo "#lnpm alias\nalias lnpm='cnpm --registry=http://localhost:7001\
|
||||
--registryweb=http://localhost:7002\
|
||||
--cache=$HOME/.npm/.cache/lnpm\
|
||||
--disturl=http://dist.u.qiniudn.com\
|
||||
--disturl=http://cnpmjs.org/dist\
|
||||
--userconfig=$HOME/.lnpmrc'" >> $HOME/.zshrc && source $HOME/.zshrc
|
||||
```
|
||||
|
||||
@@ -1,23 +1,13 @@
|
||||
# cnpmjs.org: Private npm registry and web for Enterprise
|
||||
# cnpmjs.org: Private npm registry and web for Company
|
||||
|
||||
[](http://travis-ci.org/cnpm/cnpmjs.org) [](https://coveralls.io/r/cnpm/cnpmjs.org) [](https://gemnasium.com/cnpm/cnpmjs.org)
|
||||
|
||||
[](https://nodei.co/npm/cnpmjs.org/)
|
||||
|
||||
## What is this?
|
||||
|
||||
> Private npm registry and web for Enterprise, base on [koa](http://koajs.com/), MySQL and [Simple Store Service](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide).
|
||||
|
||||
@[JacksonTian](https://github.com/JacksonTian/) had a talk about [private npm](https://speakerdeck.com/jacksontian/qi-ye-ji-node-dot-jskai-fa).
|
||||
|
||||
## Install your private npm registry
|
||||
|
||||
@see [Install and Get Started](/install).
|
||||
So `cnpm` is meaning: **Company npm**.
|
||||
|
||||
## Registry
|
||||
|
||||
* Our public registry: [r.cnpmjs.org](http://r.cnpmjs.org), syncing from [registry.npmjs.org](http://registry.npmjs.org)
|
||||
* Current [cnpmjs.org](/) version: <span id="app-version"></span>
|
||||
* Mirror of [nodejs.org/dist](http://nodejs.org/dist): [/dist mirror](/dist)
|
||||
* Mirror of [phantomjs downloads](https://bitbucket.org/ariya/phantomjs/downloads): [phantomjs mirror](/dist/phantomjs/)
|
||||
|
||||
<table class="downloads">
|
||||
<tbody>
|
||||
@@ -114,32 +104,32 @@ $(function () {
|
||||
});
|
||||
</script>
|
||||
|
||||
## cnpm cli
|
||||
## Usage
|
||||
|
||||
alias it:
|
||||
use our npm client [cnpm](https://github.com/cnpm/cnpm)(More suitable with cnpmjs.org and gzip support), you can get our client through npm:
|
||||
|
||||
```
|
||||
npm install -g cnpm --registry=http://r.cnpmjs.org
|
||||
```
|
||||
|
||||
Or you can alias NPM to use it:
|
||||
|
||||
```bash
|
||||
alias cnpm="npm --registry=http://r.cnpmjs.org \
|
||||
--cache=$HOME/.npm/.cache/cnpm \
|
||||
--disturl=http://dist.u.qiniudn.com \
|
||||
--disturl=http://cnpmjs.org/dist \
|
||||
--userconfig=$HOME/.cnpmrc"
|
||||
|
||||
#Or alias it in .bashrc or .zshrc
|
||||
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=http://r.cnpmjs.org \
|
||||
--cache=$HOME/.npm/.cache/cnpm \
|
||||
--disturl=http://dist.u.qiniudn.com \
|
||||
--disturl=http://cnpmjs.org/dist \
|
||||
--userconfig=$HOME/.cnpmrc"' >> ~/.zshrc && source ~/.zshrc
|
||||
```
|
||||
|
||||
Or you can just use our `cnpm` cli:
|
||||
|
||||
```bash
|
||||
$ npm install cnpm -g
|
||||
```
|
||||
|
||||
### install
|
||||
|
||||
Install package from [r.cnpmjs.org](http://r.cnpmjs.org). When isntall a package or version not exist, it will try to install from official registry([registry.npmjs.org](http://registry.npmjs.org)), and sync this package to cnpm in the backend.
|
||||
Install package from [r.cnpmjs.org](http://r.cnpmjs.org). When installing a package or version does not exist, it will try to install from the official registry([registry.npmjs.org](http://registry.npmjs.org)), and sync this package to cnpm in the backend.
|
||||
|
||||
```
|
||||
$ cnpm install [name]
|
||||
@@ -180,33 +170,10 @@ $ cnpm info cnpm
|
||||
|
||||
@see Github [Issues](https://github.com/cnpm/cnpmjs.org/issues)
|
||||
|
||||
## Authors
|
||||
## Histories
|
||||
|
||||
Release [History](/history).
|
||||
|
||||
```bash
|
||||
$ git summary
|
||||
|
||||
project : cnpmjs.org
|
||||
repo age : 7 weeks
|
||||
active : 132 days
|
||||
commits : 315
|
||||
files : 88
|
||||
authors :
|
||||
190 fengmk2 60.3%
|
||||
122 dead_horse 38.7%
|
||||
2 4simple 0.6%
|
||||
1 Alsotang 0.3%
|
||||
```
|
||||
|
||||
## npm and cnpm relation
|
||||
|
||||

|
||||
|
||||
## 捐赠 Donate
|
||||
如果您觉得 [cnpmjs.org] 对您有帮助,欢迎请作者一杯咖啡.
|
||||
|
||||
[](https://me.alipay.com/imk2)
|
||||
|
||||
[cnpmjs.org]: http://cnpmjs.org/
|
||||
[registry.cnpmjs.org]: http://registry.cnpmjs.org/
|
||||
|
||||
2
index.js
2
index.js
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - index.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
|
||||
@@ -21,6 +21,7 @@ var util = require('util');
|
||||
|
||||
exports.getTarballFilepath = function (filename) {
|
||||
// ensure download file path unique
|
||||
// TODO: not only .tgz, and also other extname
|
||||
var name = filename.replace(/\.tgz$/, '.' + crypto.randomBytes(16).toString('hex') + '.tgz');
|
||||
return path.join(config.uploadDir, name);
|
||||
};
|
||||
@@ -42,12 +43,12 @@ exports.isAdmin = function (username) {
|
||||
return typeof config.admins[username] === 'string';
|
||||
};
|
||||
|
||||
exports.isMaintainer = function (ctx, maintainers) {
|
||||
if (ctx.session.isAdmin) {
|
||||
exports.isMaintainer = function (user, maintainers) {
|
||||
if (user.isAdmin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var username = ctx.session.name;
|
||||
var username = user.name;
|
||||
maintainers = maintainers || [];
|
||||
var match = maintainers.filter(function (item) {
|
||||
return item.name === username;
|
||||
|
||||
@@ -15,51 +15,50 @@
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:middleware:auth');
|
||||
var User = require('../proxy/user');
|
||||
var config = require('../config');
|
||||
var common = require('../lib/common');
|
||||
var UserService = require('../services/user');
|
||||
|
||||
/**
|
||||
* Parse the request authorization
|
||||
* get the real user
|
||||
*/
|
||||
|
||||
module.exports = function (options) {
|
||||
return function *auth(next) {
|
||||
debug('%s, %s, %j', this.url, this.sessionId, this.session);
|
||||
if (!this.session) {
|
||||
// redis crash
|
||||
this.session = {};
|
||||
return yield next;
|
||||
}
|
||||
this.session.onlySync = config.enablePrivate ? true : false;
|
||||
if (this.session.name) {
|
||||
this.session.isAdmin = common.isAdmin(this.session.name);
|
||||
debug('auth exists user: %s, onlySync: %s, isAdmin: %s, headers: %j',
|
||||
this.session.name, this.session.onlySync, this.session.isAdmin, this.header);
|
||||
return yield next;
|
||||
}
|
||||
return function* auth(next) {
|
||||
this.user = {};
|
||||
|
||||
var authorization = (this.get('authorization') || '').split(' ')[1] || '';
|
||||
authorization = authorization.trim();
|
||||
debug('%s %s with %j', this.method, this.url, authorization);
|
||||
if (!authorization) {
|
||||
return yield next;
|
||||
return yield* next;
|
||||
}
|
||||
|
||||
authorization = new Buffer(authorization, 'base64').toString().split(':');
|
||||
if (authorization.length !== 2) {
|
||||
return yield next;
|
||||
return yield* next;
|
||||
}
|
||||
|
||||
var username = authorization[0];
|
||||
var password = authorization[1];
|
||||
|
||||
var row = yield User.auth(username, password);
|
||||
if (!row) {
|
||||
debug('auth fail user: %j, headers: %j', row, this.header);
|
||||
this.session.name = null;
|
||||
this.session.isAdmin = false;
|
||||
return yield next;
|
||||
var row;
|
||||
try {
|
||||
row = yield* UserService.auth(username, password);
|
||||
} catch (err) {
|
||||
// do not response error here
|
||||
// many request do not need login
|
||||
this.user.error = err;
|
||||
}
|
||||
|
||||
this.session.name = row.name;
|
||||
this.session.isAdmin = common.isAdmin(this.session.name);
|
||||
debug('auth pass user: %j, onlySync: %s, isAdmin: %s, headers: %j',
|
||||
row, this.session.onlySync, this.session.isAdmin, this.header);
|
||||
yield next;
|
||||
if (!row) {
|
||||
debug('auth fail user: %j, headers: %j', row, this.header);
|
||||
return yield* next;
|
||||
}
|
||||
|
||||
this.user.name = row.login;
|
||||
this.user.isAdmin = row.site_admin;
|
||||
this.user.scopes = row.scopes;
|
||||
debug('auth pass user: %j, headers: %j', this.user, this.header);
|
||||
yield* next;
|
||||
};
|
||||
};
|
||||
|
||||
34
middleware/limit.js
Normal file
34
middleware/limit.js
Normal file
@@ -0,0 +1,34 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/limit.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var config = require('../config');
|
||||
var limit = require('koa-limit');
|
||||
var store = require('../common/redis');
|
||||
|
||||
var limitConfig = config.limit;
|
||||
|
||||
if (!limitConfig.enable) {
|
||||
module.exports = function *ignoreLimit(next) {
|
||||
yield *next;
|
||||
};
|
||||
} else {
|
||||
|
||||
if (!config.debug) {
|
||||
limitConfig.store = store;
|
||||
}
|
||||
|
||||
module.exports = limit(limitConfig);
|
||||
}
|
||||
@@ -15,7 +15,17 @@
|
||||
*/
|
||||
|
||||
module.exports = function *login(next) {
|
||||
if (!this.session.name) {
|
||||
if (this.user.error) {
|
||||
this.status = this.user.error.status || 500;
|
||||
this.body = {
|
||||
error: this.user.error.name,
|
||||
reason: this.user.error.message
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.user.name) {
|
||||
|
||||
this.status = 401;
|
||||
this.body = {
|
||||
error: 'unauthorized',
|
||||
@@ -23,5 +33,5 @@ module.exports = function *login(next) {
|
||||
};
|
||||
return;
|
||||
}
|
||||
yield next;
|
||||
yield *next;
|
||||
};
|
||||
|
||||
@@ -30,5 +30,5 @@ module.exports = function *publishable(next) {
|
||||
this.charset = 'utf-8';
|
||||
this.body = template.replace('${host}', this.host);
|
||||
}
|
||||
yield next;
|
||||
yield *next;
|
||||
};
|
||||
|
||||
@@ -14,9 +14,14 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var util = require('util');
|
||||
var config = require('../config');
|
||||
var debug = require('debug')('cnpmjs.org:middlewares/publishable');
|
||||
|
||||
module.exports = function *publishable(next) {
|
||||
if (this.session.onlySync && !this.session.isAdmin) {
|
||||
// private mode, only admin user can publish
|
||||
// private mode, only admin user can publish
|
||||
if (config.enablePrivate && !this.user.isAdmin) {
|
||||
|
||||
this.status = 403;
|
||||
this.body = {
|
||||
error: 'no_perms',
|
||||
@@ -24,5 +29,75 @@ module.exports = function *publishable(next) {
|
||||
};
|
||||
return;
|
||||
}
|
||||
yield next;
|
||||
|
||||
// public mode, all user have permission to publish
|
||||
// but if `config.scopes` exist, only can publish with scopes in `config.scope`
|
||||
// if `config.forcePublishWithScope` set to true, only admins can publish without scope
|
||||
|
||||
var name = this.params.name || this.params[0];
|
||||
|
||||
// check if is private package list in config
|
||||
if (config.privatePackages && config.privatePackages.indexOf(name) !== -1) {
|
||||
return yield* next;
|
||||
}
|
||||
|
||||
// scope
|
||||
if (name[0] === '@') {
|
||||
if (checkScope(name, this)) {
|
||||
return yield* next;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// none-scope
|
||||
if (checkNoneScope(this)) {
|
||||
return yield* next;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* check module's scope legal
|
||||
*/
|
||||
|
||||
function checkScope(name, ctx) {
|
||||
if (!ctx.user.scopes || !ctx.user.scopes.length) {
|
||||
ctx.status = 404;
|
||||
return false;
|
||||
}
|
||||
|
||||
var scope = name.split('/')[0];
|
||||
if (ctx.user.scopes.indexOf(scope) === -1) {
|
||||
debug('assert scope %s error', name);
|
||||
ctx.status = 400;
|
||||
ctx.body = {
|
||||
error: 'invalid scope',
|
||||
reason: util.format('scope %s not match legal scopes %j', scope, ctx.user.scopes)
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if user have permission to publish without scope
|
||||
*/
|
||||
|
||||
function checkNoneScope(ctx) {
|
||||
if (!config.scopes
|
||||
|| !config.scopes.length
|
||||
|| !config.forcePublishWithScope) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// only admins can publish or unpublish non-scope modules
|
||||
if (ctx.user.isAdmin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ctx.status = 403;
|
||||
ctx.body = {
|
||||
error: 'no_perms',
|
||||
reason: 'only allow publish with ' + ctx.user.scopes.join(',') + ' scope(s)'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,11 +15,15 @@
|
||||
*/
|
||||
|
||||
module.exports = function *notFound(next) {
|
||||
yield next;
|
||||
yield *next;
|
||||
|
||||
if (this.status) {
|
||||
if (this.status && this.status !== 404) {
|
||||
return;
|
||||
}
|
||||
if (this.body && this.body.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.status = 404;
|
||||
this.body = {
|
||||
error: 'not_found',
|
||||
|
||||
29
middleware/static.js
Normal file
29
middleware/static.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/static.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var path = require('path');
|
||||
var middlewares = require('koa-middlewares');
|
||||
var config = require('../config');
|
||||
|
||||
var staticDir = path.join(path.dirname(__dirname), 'public');
|
||||
|
||||
module.exports = middlewares.staticCache(staticDir, {
|
||||
buffer: config.debug ? false : true,
|
||||
maxAge: config.debug ? 0 : 60 * 60 * 24 * 7,
|
||||
alas: {
|
||||
'/favicon.ico': '/favicon.png'
|
||||
}
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - middleware/sync_by_install.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -14,32 +14,28 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:middleware:sync_by_install');
|
||||
var config = require('../config');
|
||||
|
||||
/**
|
||||
* req.session.allowSync - allow sync triggle by cnpm install
|
||||
* this.allowSync - allow sync triggle by cnpm install
|
||||
*/
|
||||
|
||||
module.exports = function *(next) {
|
||||
module.exports = function* syncByInstall(next) {
|
||||
if (!config.syncByInstall || !config.enablePrivate) {
|
||||
// only config.enablePrivate should enable sync on install
|
||||
return yield next;
|
||||
return yield* next;
|
||||
}
|
||||
// request not by node, consider it request from web
|
||||
if (this.get('user-agent') && this.get('user-agent').indexOf('node') !== 0) {
|
||||
return yield next;
|
||||
var ua = this.get('user-agent');
|
||||
if (!ua || ua.indexOf('node') < 0) {
|
||||
return yield* next;
|
||||
}
|
||||
|
||||
this.session.allowSync = true;
|
||||
if (this.session.isAdmin) {
|
||||
// if current user is admin, should not enable auto sync on install, because it would be unpublish
|
||||
this.session.allowSync = false;
|
||||
// if request with `/xxx?write=true`, meaning the read request using for write
|
||||
if (this.query.write) {
|
||||
return yield* next;
|
||||
}
|
||||
|
||||
// TODO: allow sync will let publish sync package...
|
||||
this.session.allowSync = false;
|
||||
|
||||
debug('%s allowSync: %s', this.session.name, this.session.allowSync);
|
||||
yield next;
|
||||
this.allowSync = true;
|
||||
yield* next;
|
||||
};
|
||||
|
||||
@@ -14,14 +14,40 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
module.exports = function *notFound(next) {
|
||||
yield next;
|
||||
var debug = require('debug')('cnpmjs.org:middleware:web_not_found');
|
||||
|
||||
if (this.status) {
|
||||
module.exports = function *notFound(next) {
|
||||
yield *next;
|
||||
|
||||
if (this.status && this.status !== 404) {
|
||||
return;
|
||||
}
|
||||
if (this.body) {
|
||||
return;
|
||||
}
|
||||
|
||||
var m = /^\/([\w\-\.]+)\/?$/.exec(this.path);
|
||||
if (!m) {
|
||||
// scoped packages
|
||||
m = /^\/(@[\w\-\.]+\/[\w\-\.]+)$/.exec(this.path);
|
||||
}
|
||||
debug('%s match %j', this.url, m);
|
||||
if (m) {
|
||||
return this.redirect('/package/' + m[1]);
|
||||
}
|
||||
|
||||
// package not found
|
||||
m = /\/package\/([\w\-\_\.]+)\/?$/.exec(this.url);
|
||||
if (m) {
|
||||
var name = m[1];
|
||||
this.status = 404;
|
||||
yield* this.render('404', {
|
||||
title: 'Package - ' + name,
|
||||
name: name
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.status = 404;
|
||||
this.type = 'text/html';
|
||||
this.charset = 'utf-8';
|
||||
this.body = 'Cannot GET ' + this.path;
|
||||
};
|
||||
|
||||
102
package.json
102
package.json
@@ -1,63 +1,67 @@
|
||||
{
|
||||
"name": "cnpmjs.org",
|
||||
"version": "0.3.2",
|
||||
"version": "1.1.0",
|
||||
"description": "Private npm registry and web for Enterprise, base on MySQL and Simple Store Service",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "make test-all",
|
||||
"test": "make install && make jshint && make test",
|
||||
"start": "./bin/nodejsctl start && cp History.md docs/web/history.md",
|
||||
"status": "./bin/nodejsctl status",
|
||||
"stop": "./bin/nodejsctl stop"
|
||||
},
|
||||
"config": {
|
||||
"blanket": {
|
||||
"pattern": "//^((?!(node_modules|test|common)).)*$/"
|
||||
},
|
||||
"travis-cov": {
|
||||
"threshold": 90
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"co-read": "0.0.1",
|
||||
"co-write": "0.3.0",
|
||||
"debug": "0.7.4",
|
||||
"eventproxy": "0.2.7",
|
||||
"forward": "0.0.4",
|
||||
"giturl": "0.0.2",
|
||||
"graceful": "0.0.6",
|
||||
"gravatar": "1.0.6",
|
||||
"humanize-number": "0.0.2",
|
||||
"koa": "0.5.0",
|
||||
"koa-markdown": "0.0.2",
|
||||
"koa-middlewares": "0.0.6",
|
||||
"logfilestream": "0.1.0",
|
||||
"marked": "0.3.1",
|
||||
"microtime": "0.5.1",
|
||||
"mime": "1.2.11",
|
||||
"mkdirp": "0.3.5",
|
||||
"moment": "2.5.1",
|
||||
"ms": "0.6.2",
|
||||
"mysql": "2.1.0",
|
||||
"nodemailer": "0.6.1",
|
||||
"qn": "0.2.0",
|
||||
"ready": "0.1.1",
|
||||
"semver": "2.2.1",
|
||||
"thunkify-wrap": "0.0.1",
|
||||
"urllib": "0.5.5",
|
||||
"utility": "0.1.10"
|
||||
"bytes": "~1.0.0",
|
||||
"cfork": "~1.0.1",
|
||||
"cheerio": "~0.17.0",
|
||||
"co": "~3.1.0",
|
||||
"co-defer": "~0.1.0",
|
||||
"co-gather": "~0.0.1",
|
||||
"co-read": "~0.1.0",
|
||||
"co-redis": "~1.1.0",
|
||||
"co-urllib": "~0.2.3",
|
||||
"co-write": "~0.3.0",
|
||||
"copy-to": "~1.0.1",
|
||||
"debug": "~1.0.4",
|
||||
"error-formater": "~1.0.3",
|
||||
"eventproxy": "~0.3.1",
|
||||
"giturl": "~0.0.3",
|
||||
"graceful": "~0.1.0",
|
||||
"gravatar": "~1.0.6",
|
||||
"humanize-number": "~0.0.2",
|
||||
"koa": "~0.8.2",
|
||||
"koa-limit": "~1.0.2",
|
||||
"koa-markdown": "~0.0.3",
|
||||
"koa-middlewares": "~1.2.0",
|
||||
"marked": "~0.3.2",
|
||||
"mime": "~1.2.11",
|
||||
"mini-logger": "~0.3.0",
|
||||
"mkdirp": "~0.5.0",
|
||||
"moment": "~2.8.1",
|
||||
"ms": "~0.6.2",
|
||||
"multiline": "~0.3.4",
|
||||
"mysql": "~2.4.2",
|
||||
"nodemailer": "0.7.1",
|
||||
"qn": "~0.2.2",
|
||||
"ready": "~0.1.1",
|
||||
"redis": "~0.11.0",
|
||||
"semver": "~3.0.1",
|
||||
"thunkify-wrap": "~1.0.2",
|
||||
"utility": "~1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autod": ">=0.0.13",
|
||||
"blanket": "*",
|
||||
"autod": "~0.2.0",
|
||||
"chunkstream": "~0.0.1",
|
||||
"co-mocha": "0.0.2",
|
||||
"contributors": "*",
|
||||
"coveralls": "*",
|
||||
"mm": "0.2.0",
|
||||
"cov": "*",
|
||||
"istanbul-harmony": "*",
|
||||
"jshint": "*",
|
||||
"mm": "~0.2.1",
|
||||
"mocha": "*",
|
||||
"mocha-lcov-reporter": "*",
|
||||
"pedding": "0.0.3",
|
||||
"should": "3.1.3",
|
||||
"supertest": "0.9.0",
|
||||
"travis-cov": "*"
|
||||
"pedding": "~1.0.0",
|
||||
"should": "~4.0.4",
|
||||
"should-http": "0.0.1",
|
||||
"supertest": "~0.13.0"
|
||||
},
|
||||
"homepage": "https://github.com/cnpm/cnpmjs.org",
|
||||
"repository": {
|
||||
@@ -70,7 +74,11 @@
|
||||
"email": "fengmk2@gmail.com"
|
||||
},
|
||||
"keywords": [
|
||||
"cnpmjs.org", "npm", "npmjs", "npmjs.org", "registry"
|
||||
"cnpmjs.org",
|
||||
"npm",
|
||||
"npmjs",
|
||||
"npmjs.org",
|
||||
"registry"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 0.11.9"
|
||||
|
||||
86
proxy/dist.js
Normal file
86
proxy/dist.js
Normal file
@@ -0,0 +1,86 @@
|
||||
/**!
|
||||
* cnpmjs.org - proxy/dist.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
/* jshint -W032 */
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var path = require('path');
|
||||
var multiline = require('multiline');
|
||||
var mysql = require('../common/mysql');
|
||||
|
||||
var SAVE_FILE_SQL = multiline(function () {;/*
|
||||
INSERT INTO
|
||||
dist_file(gmt_create, gmt_modified, name, parent, date, size, url, sha1)
|
||||
VALUES
|
||||
(now(), now(), ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
name=VALUES(name),
|
||||
parent=VALUES(parent),
|
||||
date=VALUES(date),
|
||||
size=VALUES(size),
|
||||
url=VALUES(url),
|
||||
sha1=VALUES(sha1);
|
||||
*/});
|
||||
|
||||
exports.savefile = function* (info) {
|
||||
return yield mysql.query(SAVE_FILE_SQL, [
|
||||
info.name, info.parent, info.date, info.size, info.url, info.sha1
|
||||
]);
|
||||
};
|
||||
|
||||
var SAVE_DIR_SQL = multiline(function () {;/*
|
||||
INSERT INTO
|
||||
dist_dir(gmt_create, gmt_modified, name, parent, date)
|
||||
VALUES
|
||||
(now(), now(), ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
name=VALUES(name),
|
||||
parent=VALUES(parent),
|
||||
date=VALUES(date);
|
||||
*/});
|
||||
|
||||
exports.savedir = function* (info) {
|
||||
return yield mysql.query(SAVE_DIR_SQL, [
|
||||
info.name, info.parent, info.date
|
||||
]);
|
||||
};
|
||||
|
||||
var LIST_DIRS_SQL = multiline(function () {;/*
|
||||
SELECT name, parent, date FROM dist_dir WHERE parent = ?;
|
||||
*/});
|
||||
var LIST_FILES_SQL = multiline(function () {;/*
|
||||
SELECT name, parent, date, size, url, sha1
|
||||
FROM dist_file WHERE parent = ?;
|
||||
*/});
|
||||
|
||||
exports.listdir = function* (name) {
|
||||
var rs = yield [
|
||||
mysql.query(LIST_DIRS_SQL, [name]),
|
||||
mysql.query(LIST_FILES_SQL, [name]),
|
||||
];
|
||||
return rs[0].concat(rs[1]);
|
||||
};
|
||||
|
||||
var GET_FILE_SQL = multiline(function () {;/*
|
||||
SELECT name, parent, date, url, size, sha1 FROM dist_file WHERE parent = ? AND name = ?;
|
||||
*/});
|
||||
|
||||
exports.getfile = function* (fullname) {
|
||||
var name = path.basename(fullname);
|
||||
var parent = path.dirname(fullname);
|
||||
if (parent !== '/') {
|
||||
parent += '/';
|
||||
}
|
||||
return yield mysql.queryOne(GET_FILE_SQL, [parent, name]);
|
||||
};
|
||||
@@ -9,6 +9,8 @@
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
/* jshint -W032 */
|
||||
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
@@ -17,28 +19,44 @@
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var config = require('../config');
|
||||
var mysql = require('../common/mysql');
|
||||
var multiline = require('multiline');
|
||||
|
||||
var PLUS_SQL = 'INSERT INTO download_total(gmt_create, gmt_modified, \
|
||||
date, name, count) \
|
||||
VALUES(now(), now(), ?, ?, ?) \
|
||||
ON DUPLICATE KEY UPDATE gmt_modified=now(), \
|
||||
count=count + VALUES(count), name=VALUES(name), date=VALUES(date);';
|
||||
|
||||
var PLUS_SQL = multiline(function () {;/*
|
||||
INSERT INTO
|
||||
download_total(gmt_create, gmt_modified, date, name, count)
|
||||
VALUES
|
||||
(now(), now(), ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
count=count + VALUES(count),
|
||||
name=VALUES(name),
|
||||
date=VALUES(date);
|
||||
*/});
|
||||
exports.plusTotal = function (data, callback) {
|
||||
mysql.query(PLUS_SQL, [data.date, data.name, data.count], callback);
|
||||
};
|
||||
|
||||
var SELECT_ONE_TOTAL_SQL = 'SELECT date, count FROM download_total WHERE date>=? AND date<=? AND name=?;';
|
||||
|
||||
var SELECT_ONE_TOTAL_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
date, count
|
||||
FROM
|
||||
download_total
|
||||
WHERE
|
||||
date>=? AND date<=? AND name=?;
|
||||
*/});
|
||||
exports.getModuleTotal = function (name, start, end, callback) {
|
||||
mysql.query(SELECT_ONE_TOTAL_SQL, [start, end, name], callback);
|
||||
};
|
||||
|
||||
var SELECT_ALL_TOTAL_SQL = 'SELECT date, sum(count) AS count \
|
||||
FROM download_total \
|
||||
WHERE date>=? AND date<=? \
|
||||
GROUP BY date;';
|
||||
|
||||
var SELECT_ALL_TOTAL_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
date, sum(count) AS count
|
||||
FROM
|
||||
download_total
|
||||
WHERE
|
||||
date>=? AND date<=?
|
||||
GROUP BY
|
||||
date;
|
||||
*/});
|
||||
exports.getTotal = function (start, end, callback) {
|
||||
mysql.query(SELECT_ALL_TOTAL_SQL, [start, end], callback);
|
||||
};
|
||||
|
||||
490
proxy/module.js
490
proxy/module.js
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
/* jshint -W032 */
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
@@ -19,15 +20,26 @@ var utility = require('utility');
|
||||
var eventproxy = require('eventproxy');
|
||||
var config = require('../config');
|
||||
var mysql = require('../common/mysql');
|
||||
var multiline = require('multiline');
|
||||
|
||||
var MODULE_COLUMNS = 'id, publish_time, gmt_create, gmt_modified, author, name, version, description, package, dist_tarball, dist_shasum, dist_size';
|
||||
|
||||
var INSERT_MODULE_SQL = 'INSERT INTO module(gmt_create, gmt_modified, \
|
||||
publish_time, author, name, version, package, dist_tarball, dist_shasum, dist_size, description) \
|
||||
VALUES(now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?) \
|
||||
ON DUPLICATE KEY UPDATE gmt_modified=now(), publish_time=VALUES(publish_time), description=VALUES(description), \
|
||||
author=VALUES(author), name=VALUES(name), version=VALUES(version), package=VALUES(package), \
|
||||
dist_tarball=VALUES(dist_tarball), dist_shasum=VALUES(dist_shasum), dist_size=VALUES(dist_size);';
|
||||
var INSERT_MODULE_SQL = multiline(function () {;/*
|
||||
INSERT INTO
|
||||
module(gmt_create, gmt_modified, publish_time, author, name, version,
|
||||
package, dist_tarball, dist_shasum, dist_size, description)
|
||||
VALUES
|
||||
(now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
gmt_modified=now(),
|
||||
publish_time=VALUES(publish_time),
|
||||
description=VALUES(description),
|
||||
author=VALUES(author),
|
||||
name=VALUES(name),
|
||||
version=VALUES(version),
|
||||
package=VALUES(package),
|
||||
dist_tarball=VALUES(dist_tarball),
|
||||
dist_shasum=VALUES(dist_shasum),
|
||||
dist_size=VALUES(dist_size);
|
||||
*/});
|
||||
|
||||
exports.add = function (mod, callback) {
|
||||
var keywords = mod.package.keywords;
|
||||
@@ -82,7 +94,16 @@ exports.add = function (mod, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var GET_KEYWORD_SQL = 'SELECT keyword FROM module_keyword WHERE name=? ORDER BY keyword;';
|
||||
var GET_KEYWORD_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
keyword
|
||||
FROM
|
||||
module_keyword
|
||||
WHERE
|
||||
name = ?
|
||||
ORDER BY
|
||||
keyword;
|
||||
*/});
|
||||
|
||||
exports.getKeywords = function (name, callback) {
|
||||
mysql.query(GET_KEYWORD_SQL, [name], function (err, rows) {
|
||||
@@ -96,9 +117,14 @@ exports.getKeywords = function (name, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var ADD_KEYWORD_SQL = 'INSERT INTO module_keyword(gmt_create, keyword, name, description) \
|
||||
VALUES(now(), ?, ?, ?) \
|
||||
ON DUPLICATE KEY UPDATE description=VALUES(description);';
|
||||
var ADD_KEYWORD_SQL = multiline(function () {;/*
|
||||
INSERT INTO
|
||||
module_keyword(gmt_create, keyword, name, description)
|
||||
VALUES
|
||||
(now(), ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
description=VALUES(description);
|
||||
*/});
|
||||
|
||||
exports.addKeywords = function (name, description, keywords, callback) {
|
||||
var sql = '';
|
||||
@@ -124,12 +150,26 @@ exports.addKeywords = function (name, description, keywords, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var UPDATE_DESC_SQL = 'UPDATE module SET description=? WHERE id=?;';
|
||||
var UPDATE_DESC_SQL = multiline(function () {;/*
|
||||
UPDATE
|
||||
module
|
||||
SET
|
||||
description=?
|
||||
WHERE
|
||||
id=?;
|
||||
*/});
|
||||
exports.updateDescription = function (id, description, callback) {
|
||||
mysql.query(UPDATE_DESC_SQL, [description, id], callback);
|
||||
};
|
||||
|
||||
var UPDATE_PACKAGE_SQL = 'UPDATE module SET package=? WHERE id=?;';
|
||||
var UPDATE_PACKAGE_SQL = multiline(function () {;/*
|
||||
UPDATE
|
||||
module
|
||||
SET
|
||||
package=?
|
||||
WHERE
|
||||
id=?;
|
||||
*/});
|
||||
exports.updateReadme = function (id, readme, callback) {
|
||||
exports.getById(id, function (err, data) {
|
||||
if (err) {
|
||||
@@ -142,9 +182,7 @@ exports.updateReadme = function (id, readme, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var UPDATE_DIST_SQL = 'UPDATE module SET publish_time=?, version=?, package=?, \
|
||||
dist_tarball=?, dist_shasum=?, dist_size=? WHERE id=?;';
|
||||
|
||||
var UPDATE_DIST_SQL = 'UPDATE module SET ? WHERE id=?';
|
||||
exports.update = function (mod, callback) {
|
||||
var pkg;
|
||||
try {
|
||||
@@ -153,8 +191,18 @@ exports.update = function (mod, callback) {
|
||||
return callback(e);
|
||||
}
|
||||
var dist = mod.package.dist;
|
||||
|
||||
var arg = {
|
||||
publish_time: mod.publish_time,
|
||||
version: mod.version,
|
||||
package: pkg,
|
||||
dist_tarball: dist.tarball,
|
||||
dist_shasum: dist.shasum,
|
||||
dist_size: dist.size
|
||||
};
|
||||
|
||||
mysql.query(UPDATE_DIST_SQL,
|
||||
[mod.publish_time, mod.version, pkg, dist.tarball, dist.shasum, dist.size, mod.id],
|
||||
[arg, mod.id],
|
||||
function (err, result) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
@@ -182,8 +230,16 @@ function stringifyPackage(pkg) {
|
||||
return encodeURIComponent(JSON.stringify(pkg));
|
||||
}
|
||||
|
||||
var SELECT_MODULE_BY_ID_SQL = 'SELECT ' + MODULE_COLUMNS + ' FROM module WHERE id=?;';
|
||||
|
||||
var SELECT_MODULE_BY_ID_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
id, publish_time, gmt_create, gmt_modified, author, name,
|
||||
version, description, package, dist_tarball, dist_shasum, dist_size
|
||||
FROM
|
||||
module
|
||||
WHERE
|
||||
id=?;
|
||||
*/});
|
||||
exports.getById = function (id, callback) {
|
||||
id = Number(id);
|
||||
mysql.queryOne(SELECT_MODULE_BY_ID_SQL, [id], function (err, row) {
|
||||
@@ -200,8 +256,15 @@ exports.getById = function (id, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var SELECT_MODULE_SQL = 'SELECT ' + MODULE_COLUMNS + ' FROM module WHERE name=? AND version=?;';
|
||||
|
||||
var SELECT_MODULE_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
id, publish_time, gmt_create, gmt_modified, author, name,
|
||||
version, description, package, dist_tarball, dist_shasum, dist_size
|
||||
FROM
|
||||
module
|
||||
WHERE
|
||||
name=? AND version=?;
|
||||
*/});
|
||||
exports.get = function (name, version, callback) {
|
||||
mysql.queryOne(SELECT_MODULE_SQL, [name, version], function (err, row) {
|
||||
if (err || !row) {
|
||||
@@ -217,14 +280,26 @@ exports.get = function (name, version, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var INSERT_TAG_SQL = 'INSERT INTO tag(gmt_create, gmt_modified, \
|
||||
name, tag, version, module_id) \
|
||||
VALUES(now(), now(), ?, ?, ?, ?) \
|
||||
ON DUPLICATE KEY UPDATE gmt_modified=now(), module_id=VALUES(module_id), \
|
||||
name=VALUES(name), tag=VALUES(tag), version=VALUES(version);';
|
||||
|
||||
var SELECT_MODULE_ID_SQL = 'SELECT id FROM module WHERE name=? AND version=?;';
|
||||
|
||||
var SELECT_MODULE_ID_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
module
|
||||
WHERE
|
||||
name=? AND version=?;
|
||||
*/});
|
||||
var INSERT_TAG_SQL = multiline(function () {;/*
|
||||
INSERT INTO
|
||||
tag(gmt_create, gmt_modified, name, tag, version, module_id)
|
||||
VALUES
|
||||
(now(), now(), ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
gmt_modified=now(),
|
||||
module_id=VALUES(module_id),
|
||||
name=VALUES(name),
|
||||
tag=VALUES(tag),
|
||||
version=VALUES(version);
|
||||
*/});
|
||||
exports.addTag = function (name, tag, version, callback) {
|
||||
mysql.queryOne(SELECT_MODULE_ID_SQL, [name, version], function (err, row) {
|
||||
if (err) {
|
||||
@@ -240,8 +315,14 @@ exports.addTag = function (name, tag, version, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var SELECT_TAG_SQL = 'SELECT tag, version, gmt_modified, module_id FROM tag WHERE name=? AND tag=?;';
|
||||
|
||||
var SELECT_TAG_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
tag, version, gmt_modified, module_id
|
||||
FROM
|
||||
tag
|
||||
WHERE
|
||||
name=? AND tag=?;
|
||||
*/});
|
||||
exports.getByTag = function (name, tag, callback) {
|
||||
mysql.queryOne(SELECT_TAG_SQL, [name, tag], function (err, row) {
|
||||
if (err || !row) {
|
||||
@@ -251,26 +332,51 @@ exports.getByTag = function (name, tag, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var DELETE_TAGS_SQL = 'DELETE FROM tag WHERE name=?;';
|
||||
|
||||
var DELETE_TAGS_SQL = multiline(function () {;/*
|
||||
DELETE FROM
|
||||
tag
|
||||
WHERE
|
||||
name=?;
|
||||
*/});
|
||||
exports.removeTags = function (name, callback) {
|
||||
mysql.query(DELETE_TAGS_SQL, [name], callback);
|
||||
};
|
||||
|
||||
var DELETE_TAGS_BY_IDS_SQL = 'DELETE FROM tag WHERE id in (?)';
|
||||
var DELETE_TAGS_BY_IDS_SQL = multiline(function () {;/*
|
||||
DELETE FROM
|
||||
tag
|
||||
WHERE
|
||||
id IN (?);
|
||||
*/});
|
||||
exports.removeTagsByIds = function (ids, callback) {
|
||||
mysql.query(DELETE_TAGS_BY_IDS_SQL, [ids], callback);
|
||||
};
|
||||
|
||||
var SELECT_ALL_TAGS_SQL = 'SELECT id, tag, version, gmt_modified, module_id FROM tag WHERE name=?;';
|
||||
|
||||
var SELECT_ALL_TAGS_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
id, tag, version, gmt_modified, module_id
|
||||
FROM
|
||||
tag
|
||||
WHERE
|
||||
name=?;
|
||||
*/});
|
||||
exports.listTags = function (name, callback) {
|
||||
mysql.query(SELECT_ALL_TAGS_SQL, [name], callback);
|
||||
};
|
||||
|
||||
var SELECT_LATEST_MODULE_SQL = 'SELECT ' + MODULE_COLUMNS +
|
||||
' FROM module WHERE name=? AND version <> "next" ORDER BY publish_time DESC LIMIT 1;';
|
||||
|
||||
var SELECT_LATEST_MODULE_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
id, publish_time, gmt_create, gmt_modified, author, name,
|
||||
version, description, package, dist_tarball, dist_shasum, dist_size
|
||||
FROM
|
||||
module
|
||||
WHERE
|
||||
name=? AND version <> "next"
|
||||
ORDER BY
|
||||
publish_time DESC
|
||||
LIMIT
|
||||
1;
|
||||
*/});
|
||||
exports.getLatest = function (name, callback) {
|
||||
exports.getByTag(name, 'latest', function (err, row) {
|
||||
if (err || row) {
|
||||
@@ -293,8 +399,17 @@ exports.getLatest = function (name, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var LIST_MODULE_SQL = 'SELECT ' + MODULE_COLUMNS + ' FROM module WHERE name=? ORDER BY id DESC;';
|
||||
|
||||
var LIST_MODULE_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
id, publish_time, gmt_create, gmt_modified, author, name,
|
||||
version, description, package, dist_tarball, dist_shasum, dist_size
|
||||
FROM
|
||||
module
|
||||
WHERE
|
||||
name=?
|
||||
ORDER BY
|
||||
id DESC;
|
||||
*/});
|
||||
exports.listByName = function (name, callback) {
|
||||
mysql.query(LIST_MODULE_SQL, [name], function (err, rows) {
|
||||
if (err) {
|
||||
@@ -314,82 +429,106 @@ exports.listByName = function (name, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var LIST_SINCE_SQLS = [
|
||||
'SELECT module_id FROM tag WHERE tag="latest" AND gmt_modified>?',
|
||||
'SELECT name, package FROM module WHERE id IN (?);'
|
||||
];
|
||||
var LIST_SINCE_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
distinct(name)
|
||||
FROM
|
||||
tag
|
||||
WHERE
|
||||
gmt_modified > ?;
|
||||
*/});
|
||||
exports.listSince = function (start, callback) {
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(callback);
|
||||
mysql.query(LIST_SINCE_SQLS[0], [new Date(start)], ep.done(function (rows) {
|
||||
if (!rows || rows.length === 0) {
|
||||
return callback(null, []);
|
||||
}
|
||||
ep.emit('ids', rows.map(function (r) {
|
||||
return r.module_id;
|
||||
}));
|
||||
}));
|
||||
|
||||
ep.once('ids', function (ids) {
|
||||
mysql.query(LIST_SINCE_SQLS[1], [ids], callback);
|
||||
});
|
||||
mysql.query(LIST_SINCE_SQL, [new Date(start)], callback);
|
||||
};
|
||||
|
||||
var LIST_SHORT_SQL = 'SELECT distinct(name) FROM tag ORDER BY name';
|
||||
var LIST_ALL_NAME_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
distinct(name)
|
||||
FROM
|
||||
module;
|
||||
*/});
|
||||
exports.listAllNames = function (callback) {
|
||||
mysql.query(LIST_ALL_NAME_SQL, [], callback);
|
||||
};
|
||||
|
||||
var LIST_SHORT_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
distinct(name)
|
||||
FROM
|
||||
tag
|
||||
ORDER BY
|
||||
name;
|
||||
*/});
|
||||
exports.listShort = function (callback) {
|
||||
mysql.query(LIST_SHORT_SQL, callback);
|
||||
};
|
||||
|
||||
var LIST_ALL_MODULE_NAMES_SQL = 'SELECT distinct(name) FROM module ORDER BY name';
|
||||
var LIST_ALL_MODULE_NAMES_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
distinct(name)
|
||||
FROM
|
||||
module
|
||||
ORDER BY
|
||||
name;
|
||||
*/});
|
||||
exports.listAllModuleNames = function (callback) {
|
||||
mysql.query(LIST_ALL_MODULE_NAMES_SQL, callback);
|
||||
};
|
||||
|
||||
var DELETE_MODULE_BY_NAME_SQL = 'DELETE FROM module WHERE name=?;';
|
||||
var DELETE_MODULE_BY_NAME_SQL = multiline(function () {;/*
|
||||
DELETE FROM
|
||||
module
|
||||
WHERE
|
||||
name=?;
|
||||
*/});
|
||||
exports.removeByName = function (name, callback) {
|
||||
mysql.query(DELETE_MODULE_BY_NAME_SQL, [name], callback);
|
||||
};
|
||||
|
||||
var DELETE_MODULE_BY_NAME_AND_VERSIONS_SQL = 'DELETE FROM module WHERE name=? AND version IN(?);';
|
||||
var DELETE_MODULE_BY_NAME_AND_VERSIONS_SQL = multiline(function () {;/*
|
||||
DELETE FROM
|
||||
module
|
||||
WHERE
|
||||
name=? AND version in(?);
|
||||
*/});
|
||||
exports.removeByNameAndVersions = function (name, versions, callback) {
|
||||
mysql.query(DELETE_MODULE_BY_NAME_AND_VERSIONS_SQL, [name, versions], callback);
|
||||
};
|
||||
|
||||
var LIST_BY_AUTH_SQLS = [
|
||||
'SELECT distinct(name) AS name FROM module WHERE author = ? ORDER BY publish_time DESC LIMIT 100;',
|
||||
'SELECT module_id FROM tag WHERE tag="latest" AND name IN (?)',
|
||||
'SELECT name, description FROM module WHERE id IN (?) ORDER BY publish_time DESC'
|
||||
];
|
||||
exports.listByAuthor = function (author, callback) {
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(callback);
|
||||
mysql.query(LIST_BY_AUTH_SQLS[0], [author], ep.done(function (rows) {
|
||||
if (!rows || rows.length === 0) {
|
||||
return callback(null, []);
|
||||
}
|
||||
ep.emit('names', rows.map(function (r) {
|
||||
return r.name;
|
||||
}));
|
||||
}));
|
||||
ep.on('names', function (names) {
|
||||
mysql.query(LIST_BY_AUTH_SQLS[1], [names], ep.done(function (rows) {
|
||||
if (!rows || rows.length === 0) {
|
||||
return callback(null, []);
|
||||
}
|
||||
ep.emit('ids', rows.map(function (r) {
|
||||
return r.module_id;
|
||||
}));
|
||||
}));
|
||||
});
|
||||
ep.on('ids', function (ids) {
|
||||
mysql.query(LIST_BY_AUTH_SQLS[2], [ids], callback);
|
||||
});
|
||||
};
|
||||
|
||||
var SEARCH_MODULES_SQL = 'SELECT module_id FROM tag WHERE name LIKE ? AND tag="latest" ORDER BY name LIMIT ?;';
|
||||
var SEARCH_MODULES_BY_KEYWORD_SQL = 'SELECT name, description FROM module_keyword WHERE keyword = ? ORDER BY id DESC LIMIT ?;';
|
||||
var QUERY_MODULES_BY_ID_SQL = 'SELECT name, description FROM module WHERE id IN (?) ORDER BY name;';
|
||||
|
||||
var SEARCH_MODULES_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
module_id
|
||||
FROM
|
||||
tag
|
||||
WHERE
|
||||
name LIKE ? AND tag="latest"
|
||||
ORDER BY
|
||||
name
|
||||
LIMIT
|
||||
?;
|
||||
*/});
|
||||
var SEARCH_MODULES_BY_KEYWORD_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
name, description
|
||||
FROM
|
||||
module_keyword
|
||||
WHERE
|
||||
keyword=?
|
||||
ORDER BY
|
||||
id DESC
|
||||
LIMIT
|
||||
?;
|
||||
*/});
|
||||
var QUERY_MODULES_BY_ID_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
name, description
|
||||
FROM
|
||||
module
|
||||
WHERE
|
||||
id IN (?)
|
||||
ORDER BY
|
||||
name;
|
||||
*/});
|
||||
exports.search = function (word, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
@@ -452,3 +591,158 @@ exports.search = function (word, options, callback) {
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
var GET_LAST_MODIFIED_MODULE_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
id, gmt_modified
|
||||
FROM
|
||||
module
|
||||
WHERE
|
||||
name=?
|
||||
ORDER BY
|
||||
gmt_modified DESC
|
||||
LIMIT 1;
|
||||
*/});
|
||||
exports.getLastModified = function* (name) {
|
||||
var row = yield mysql.queryOne(GET_LAST_MODIFIED_MODULE_SQL, [name]);
|
||||
return row && row.gmt_modified;
|
||||
};
|
||||
|
||||
var UPDATE_LAST_MODIFIED_SQL = 'UPDATE module SET gmt_modified=now() WHERE id=?;';
|
||||
exports.updateLastModified = function* (name) {
|
||||
var row = yield mysql.queryOne(GET_LAST_MODIFIED_MODULE_SQL, [name]);
|
||||
if (row) {
|
||||
yield mysql.query(UPDATE_LAST_MODIFIED_SQL, [row.id]);
|
||||
}
|
||||
};
|
||||
|
||||
var DELETE_TAGS_BY_NAMES_SQL = 'DELETE FROM tag WHERE name=? AND tag IN (?);';
|
||||
exports.removeTagsByNames = function* (moduleName, tagNames) {
|
||||
return yield mysql.query(DELETE_TAGS_BY_NAMES_SQL, [moduleName, tagNames]);
|
||||
};
|
||||
|
||||
/**
|
||||
* forward compatbility for update from lower version cnpmjs.org
|
||||
* redirect @scope/name => name
|
||||
*/
|
||||
exports.getAdaptName = function* (name) {
|
||||
if (!config.scopes
|
||||
|| !config.scopes.length
|
||||
|| !config.adaptScope) {
|
||||
return;
|
||||
}
|
||||
|
||||
var tmp = name.split('/');
|
||||
var scope = tmp[0];
|
||||
name = tmp[1];
|
||||
|
||||
if (config.scopes.indexOf(scope) === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var pkg = yield exports.getByTag(name, 'latest');
|
||||
// only private module can adapt
|
||||
if (pkg && pkg.package._publish_on_cnpm) {
|
||||
return name;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
exports.listPrivates = function* () {
|
||||
var scopes = config.scopes;
|
||||
if (!scopes || !scopes.length) {
|
||||
return [];
|
||||
}
|
||||
var privatePackages = config.privatePackages || [];
|
||||
|
||||
var args = [];
|
||||
var sql = 'SELECT module_id AS id FROM tag WHERE tag="latest" AND (';
|
||||
var wheres = [];
|
||||
|
||||
scopes.forEach(function (scope) {
|
||||
wheres.push('name LIKE ?');
|
||||
args.push(scope + '%');
|
||||
});
|
||||
|
||||
if (privatePackages.length) {
|
||||
wheres.push('name in (?)');
|
||||
args.push(privatePackages);
|
||||
}
|
||||
|
||||
sql = sql + wheres.join(' OR ') + ')';
|
||||
|
||||
var ids = yield mysql.query(sql, args);
|
||||
ids = ids.map(function (row) {
|
||||
return row.id;
|
||||
});
|
||||
|
||||
if (!ids.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return yield mysql.query(QUERY_MODULES_BY_ID_SQL, [ids]);
|
||||
};
|
||||
|
||||
var LIST_BY_AUTH_SQLS = [];
|
||||
LIST_BY_AUTH_SQLS.push(multiline(function () {;/*
|
||||
SELECT
|
||||
distinct(name) AS name
|
||||
FROM
|
||||
module
|
||||
WHERE
|
||||
author=?
|
||||
ORDER BY
|
||||
publish_time DESC
|
||||
LIMIT
|
||||
100;
|
||||
*/}));
|
||||
LIST_BY_AUTH_SQLS.push(multiline(function () {;/*
|
||||
SELECT
|
||||
name
|
||||
FROM
|
||||
module_maintainer
|
||||
WHERE
|
||||
user = ?
|
||||
*/}));
|
||||
LIST_BY_AUTH_SQLS.push(multiline(function () {;/*
|
||||
SELECT
|
||||
module_id
|
||||
FROM
|
||||
tag
|
||||
WHERE
|
||||
tag="latest" AND name IN (?);
|
||||
*/}));
|
||||
LIST_BY_AUTH_SQLS.push(multiline(function () {;/*
|
||||
SELECT
|
||||
name, description
|
||||
FROM
|
||||
module
|
||||
WHERE
|
||||
id IN (?)
|
||||
ORDER BY
|
||||
publish_time DESC;
|
||||
*/}));
|
||||
exports.listByAuthor = function* (author, callback) {
|
||||
var names = yield [
|
||||
mysql.query(LIST_BY_AUTH_SQLS[0], [author]),
|
||||
mysql.query(LIST_BY_AUTH_SQLS[1], [author])
|
||||
];
|
||||
|
||||
names = names[0].concat(names[1]).map(function (n) {
|
||||
return n.name;
|
||||
}).sort();
|
||||
|
||||
if (!names.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var ids = yield mysql.query(LIST_BY_AUTH_SQLS[2], [names]);
|
||||
if (!ids.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
ids = ids.map(function (i) {
|
||||
return i.module_id;
|
||||
});
|
||||
return yield mysql.query(LIST_BY_AUTH_SQLS[3], [ids]);
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
/* jshint -W032 */
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
@@ -16,16 +17,26 @@
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var mysql = require('../common/mysql');
|
||||
var multiline = require('multiline');
|
||||
|
||||
var LIST_DEPS_SQL = 'SELECT deps FROM module_deps WHERE name=?;';
|
||||
|
||||
var LIST_DEPS_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
deps
|
||||
FROM
|
||||
module_deps
|
||||
WHERE
|
||||
name=?;
|
||||
*/});
|
||||
exports.list = function (name, callback) {
|
||||
mysql.query(LIST_DEPS_SQL, [name], callback);
|
||||
};
|
||||
|
||||
var INSERT_DEPS_SQL = 'INSERT INTO module_deps(gmt_create, name, deps) \
|
||||
VALUES(now(), ?, ?);';
|
||||
|
||||
var INSERT_DEPS_SQL = multiline(function () {;/*
|
||||
INSERT INTO
|
||||
module_deps(gmt_create, name, deps)
|
||||
VALUES
|
||||
(now(), ?, ?);
|
||||
*/});
|
||||
exports.add = function (name, deps, callback) {
|
||||
mysql.query(INSERT_DEPS_SQL, [name, deps], function (err, result) {
|
||||
if (err && err.code === 'ER_DUP_ENTRY') {
|
||||
@@ -35,8 +46,12 @@ exports.add = function (name, deps, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var DELETE_DEPS_SQL = 'DELETE FROM module_deps WHERE name=? AND deps=?;';
|
||||
|
||||
var DELETE_DEPS_SQL = multiline(function () {;/*
|
||||
DELETE FROM
|
||||
module_deps
|
||||
WHERE
|
||||
name=? AND deps=?;
|
||||
*/});
|
||||
exports.remove = function (name, deps, callback) {
|
||||
mysql.query(DELETE_DEPS_SQL, [name, deps], callback);
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
/* jshint -W032 */
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
@@ -17,11 +18,17 @@
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var mysql = require('../common/mysql');
|
||||
|
||||
var INSERT_LOG_SQL = 'INSERT INTO module_log(gmt_create, gmt_modified, name, username, log) \
|
||||
VALUES(now(), now(), ?, ?, "");';
|
||||
|
||||
var INSERT_LOG_SQL = 'INSERT INTO module_log SET ?';
|
||||
exports.create = function (data, callback) {
|
||||
mysql.query(INSERT_LOG_SQL, [data.name, data.username], function (err, result) {
|
||||
var now = new Date();
|
||||
var args = {
|
||||
gmt_create: now,
|
||||
gmt_modified: now,
|
||||
name: data.name,
|
||||
username: data.username,
|
||||
log: ''
|
||||
};
|
||||
mysql.query(INSERT_LOG_SQL, [args], function (err, result) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
@@ -29,7 +36,7 @@ exports.create = function (data, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var APPEND_SQL = 'UPDATE module_log SET log=CONCAT(log, ?), gmt_modified=now() WHERE id=?;';
|
||||
var APPEND_SQL = 'UPDATE module_log SET log=CONCAT(log, ?), gmt_modified=now() WHERE id=?';
|
||||
exports.append = function (id, log, callback) {
|
||||
log = '\n' + log;
|
||||
mysql.query(APPEND_SQL, [log, id], function (err) {
|
||||
@@ -37,7 +44,7 @@ exports.append = function (id, log, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var SELECT_SQL = 'SELECT * FROM module_log WHERE id=?;';
|
||||
var SELECT_SQL = 'SELECT * FROM module_log WHERE id=?';
|
||||
exports.get = function (id, callback) {
|
||||
mysql.queryOne(SELECT_SQL, [id], callback);
|
||||
};
|
||||
|
||||
92
proxy/module_maintainer.js
Normal file
92
proxy/module_maintainer.js
Normal file
@@ -0,0 +1,92 @@
|
||||
/**!
|
||||
* cnpmjs.org - proxy/module_maintainer.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:proxy:module_maintainer');
|
||||
var mysql = require('../common/mysql');
|
||||
var Module = require('./module');
|
||||
|
||||
var GET_MAINTANINERS_SQL = 'SELECT user FROM module_maintainer WHERE name = ?;';
|
||||
|
||||
exports.get = function* (name) {
|
||||
var users = yield mysql.query(GET_MAINTANINERS_SQL, [name]);
|
||||
return users.map(function (row) {
|
||||
return row.user;
|
||||
});
|
||||
};
|
||||
|
||||
var ADD_SQL = 'INSERT INTO module_maintainer(name, user, gmt_create) \
|
||||
VALUES (?, ?, now());';
|
||||
function* add(name, username) {
|
||||
try {
|
||||
yield mysql.query(ADD_SQL, [name, username]);
|
||||
} catch (err) {
|
||||
if (err.code !== 'ER_DUP_ENTRY') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var REMOVE_SQL = 'DELETE FROM module_maintainer WHERE name = ? AND user IN (?);';
|
||||
function* remove(name, usernames) {
|
||||
return yield mysql.query(REMOVE_SQL, [name, usernames]);
|
||||
}
|
||||
|
||||
var REMOVE_ALL_SQL = 'DELETE FROM module_maintainer WHERE name = ?';
|
||||
|
||||
exports.removeAll = function* (name) {
|
||||
return yield mysql.query(REMOVE_ALL_SQL, [name]);
|
||||
};
|
||||
|
||||
exports.addMulti = function* (name, usernames) {
|
||||
var tasks = [];
|
||||
for (var i = 0; i < usernames.length; i++) {
|
||||
tasks.push(add(name, usernames[i]));
|
||||
}
|
||||
return yield tasks;
|
||||
};
|
||||
|
||||
exports.update = function* (name, maintainers) {
|
||||
// maintainers should be [name1, name2, ...] format
|
||||
// find out the exists maintainers then remove the deletes and add the left
|
||||
if (maintainers.length === 0) {
|
||||
return {
|
||||
add: [],
|
||||
remove: []
|
||||
};
|
||||
}
|
||||
var exists = yield* exports.get(name);
|
||||
var addUsers = maintainers;
|
||||
var removeUsers = [];
|
||||
if (exists.length > 0) {
|
||||
for (var i = 0; i < exists.length; i++) {
|
||||
var username = exists[i];
|
||||
if (addUsers.indexOf(username) === -1) {
|
||||
removeUsers.push(username);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yield* exports.addMulti(name, addUsers);
|
||||
// make sure all add users success then remove users
|
||||
if (removeUsers.length > 0) {
|
||||
yield* remove(name, removeUsers);
|
||||
}
|
||||
debug('add %d users, remove %d users', addUsers.length, removeUsers.length);
|
||||
return {
|
||||
add: addUsers,
|
||||
remove: removeUsers
|
||||
};
|
||||
};
|
||||
74
proxy/module_star.js
Normal file
74
proxy/module_star.js
Normal file
@@ -0,0 +1,74 @@
|
||||
/**!
|
||||
* cnpmjs.org - proxy/module_star.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
/* jshint -W032 */
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var mysql = require('../common/mysql');
|
||||
var multiline = require('multiline');
|
||||
|
||||
var ADD_SQL = multiline(function () {;/*
|
||||
INSERT INTO
|
||||
module_star(name, user, gmt_create)
|
||||
VALUES
|
||||
(?, ?, now());
|
||||
*/});
|
||||
exports.add = function *add(name, user) {
|
||||
try {
|
||||
yield mysql.query(ADD_SQL, [name, user]);
|
||||
} catch (err) {
|
||||
if (err.code !== 'ER_DUP_ENTRY') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var REMOVE_SQL = multiline(function () {;/*
|
||||
DELETE FROM
|
||||
module_star
|
||||
WHERE
|
||||
name = ? AND user = ?;
|
||||
*/});
|
||||
exports.remove = function *(name, user) {
|
||||
return yield mysql.query(REMOVE_SQL, [name, user]);
|
||||
};
|
||||
|
||||
var LIST_USERS_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
user
|
||||
FROM
|
||||
module_star
|
||||
WHERE
|
||||
name = ?;
|
||||
*/});
|
||||
exports.listUsers = function *(name) {
|
||||
var rows = yield mysql.query(LIST_USERS_SQL, [name]);
|
||||
return rows.map(function (r) {
|
||||
return r.user;
|
||||
});
|
||||
};
|
||||
|
||||
var LIST_USER_MODULES_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
name
|
||||
FROM
|
||||
module_star
|
||||
WHERE
|
||||
user = ?;
|
||||
*/});
|
||||
exports.listUserModules = function *(user) {
|
||||
return (yield mysql.query(LIST_USER_MODULES_SQL, [user])).map(function (r) {
|
||||
return r.name;
|
||||
});
|
||||
};
|
||||
44
proxy/module_unpublished.js
Normal file
44
proxy/module_unpublished.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/**!
|
||||
* cnpmjs.org - proxy/module_unpublished.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
/* jshint -W032 */
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var multiline = require('multiline');
|
||||
var mysql = require('../common/mysql');
|
||||
|
||||
var SAVE_SQL = multiline(function () {;/*
|
||||
INSERT INTO
|
||||
module_unpublished(gmt_create, gmt_modified, name, package)
|
||||
VALUES
|
||||
(now(), now(), ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
gmt_modified=now(),
|
||||
name=VALUES(name),
|
||||
package=VALUES(package);
|
||||
*/});
|
||||
|
||||
exports.add = function* (name, pkg) {
|
||||
return yield mysql.query(SAVE_SQL, [name, JSON.stringify(pkg)]);
|
||||
};
|
||||
|
||||
var GET_SQL = 'SELECT gmt_modified, name, package FROM module_unpublished WHERE name=?;';
|
||||
|
||||
exports.get = function* (name) {
|
||||
var row = yield mysql.queryOne(GET_SQL, [name]);
|
||||
if (row) {
|
||||
row.package = JSON.parse(row.package);
|
||||
}
|
||||
return row;
|
||||
};
|
||||
93
proxy/npm.js
93
proxy/npm.js
@@ -14,56 +14,67 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var urllib = require('urllib');
|
||||
var urllib = require('co-urllib');
|
||||
var config = require('../config');
|
||||
|
||||
function request(url, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
var USER_AGENT = 'cnpmjs.org/' + config.version + ' ' + urllib.USER_AGENT;
|
||||
|
||||
function* request(url, options) {
|
||||
options = options || {};
|
||||
options.dataType = options.dataType || 'json';
|
||||
options.timeout = options.timeout || 120000;
|
||||
url = config.sourceNpmRegistry + url;
|
||||
urllib.request(url, options, function (err, data, res) {
|
||||
if (err) {
|
||||
var statusCode = res && res.statusCode || -1;
|
||||
if (err.name === 'JSONResponseFormatError' && statusCode >= 500) {
|
||||
err.name = 'NPMServerError';
|
||||
err.message = 'Status ' + statusCode + ', ' + (data && data.toString() || 'empty body');
|
||||
}
|
||||
options.headers = {
|
||||
'user-agent': USER_AGENT
|
||||
};
|
||||
var registry = options.registry || config.sourceNpmRegistry;
|
||||
url = registry + url;
|
||||
var r;
|
||||
try {
|
||||
r = yield* urllib.request(url, options);
|
||||
} catch (err) {
|
||||
var statusCode = err.status || -1;
|
||||
var data = err.data || '[empty]';
|
||||
if (err.name === 'JSONResponseFormatError' && statusCode >= 500) {
|
||||
err.name = 'NPMServerError';
|
||||
err.message = 'Status ' + statusCode + ', ' + data.toString();
|
||||
}
|
||||
callback(err, data, res);
|
||||
});
|
||||
throw err;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
exports.get = function (name, callback) {
|
||||
request('/' + name, function (err, data, res) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
res = res || {};
|
||||
if (res.statusCode === 404) {
|
||||
data = null;
|
||||
}
|
||||
callback(null, data, res);
|
||||
exports.request = request;
|
||||
|
||||
exports.getUser = function *(name) {
|
||||
var url = '/-/user/org.couchdb.user:' + name;
|
||||
var r = yield *request(url);
|
||||
var data = r.data;
|
||||
if (data && !data.name) {
|
||||
data = null;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
exports.get = function *(name) {
|
||||
var r = yield *request('/' + name);
|
||||
var data = r.data;
|
||||
if (r.status === 404) {
|
||||
data = null;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
exports.getAllSince = function *(startkey) {
|
||||
var r = yield *request('/-/all/since?stale=update_after&startkey=' + startkey, {
|
||||
timeout: 300000
|
||||
});
|
||||
return r.data;
|
||||
};
|
||||
|
||||
exports.getAllSince = function (startkey, callback) {
|
||||
request('/-/all/since?stale=update_after&startkey=' + startkey, {
|
||||
dataType: 'json',
|
||||
timeout: 300000
|
||||
}, callback);
|
||||
exports.getShort = function *() {
|
||||
var r = yield *request('/-/short', {
|
||||
timeout: 300000,
|
||||
registry: 'http://r.cnpmjs.org', // registry.npmjs.org/-/short is 404 now.
|
||||
});
|
||||
return r.data;
|
||||
};
|
||||
|
||||
exports.getShort = function (callback) {
|
||||
request('/-/short', {
|
||||
dataType: 'json',
|
||||
timeout: 300000
|
||||
}, callback);
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
/* jshint -W032 */
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
@@ -18,13 +19,22 @@ var thunkify = require('thunkify-wrap');
|
||||
var eventproxy = require('eventproxy');
|
||||
var config = require('../config');
|
||||
var mysql = require('../common/mysql');
|
||||
var multiline = require('multiline');
|
||||
|
||||
var DB_SIZE_SQL = 'SELECT TABLE_NAME AS name, data_length, index_length \
|
||||
FROM information_schema.tables \
|
||||
WHERE TABLE_SCHEMA = ? \
|
||||
GROUP BY TABLE_NAME \
|
||||
ORDER BY data_length DESC \
|
||||
LIMIT 0 , 200';
|
||||
var DB_SIZE_SQL = multiline(function () {;/*
|
||||
SELECT
|
||||
TABLE_NAME AS name, data_length, index_length
|
||||
FROM
|
||||
information_schema.tables
|
||||
WHERE
|
||||
TABLE_SCHEMA = ?
|
||||
GROUP BY
|
||||
TABLE_NAME
|
||||
ORDER BY
|
||||
data_length DESC
|
||||
LIMIT
|
||||
0, 200;
|
||||
*/});
|
||||
|
||||
var TOTAL_MODULE_SQL = 'SELECT count(distinct(name)) AS count FROM module;';
|
||||
var TOTAL_VERSION_SQL = 'SELECT count(name) AS count FROM module;';
|
||||
@@ -84,7 +94,14 @@ exports.get = function (callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var PLUS_DELETE_MODULE_SQL = 'UPDATE total SET module_delete=module_delete+1 WHERE name="total";';
|
||||
var PLUS_DELETE_MODULE_SQL = multiline(function () {;/*
|
||||
UPDATE
|
||||
total
|
||||
SET
|
||||
module_delete=module_delete+1
|
||||
WHERE
|
||||
name="total";
|
||||
*/});
|
||||
exports.plusDeleteModule = function (callback) {
|
||||
mysql.query(PLUS_DELETE_MODULE_SQL, callback);
|
||||
};
|
||||
@@ -93,31 +110,61 @@ exports.getTotalInfo = function (callback) {
|
||||
mysql.queryOne(TOTAL_INFO_SQL, callback);
|
||||
};
|
||||
|
||||
var SET_LAST_SYNC_TIME_SQL = 'UPDATE total SET last_sync_time=? WHERE name="total";';
|
||||
var SET_LAST_SYNC_TIME_SQL = multiline(function () {;/*
|
||||
UPDATE
|
||||
total
|
||||
SET
|
||||
last_sync_time=?
|
||||
WHERE
|
||||
name="total";
|
||||
*/});
|
||||
exports.setLastSyncTime = function (time, callback) {
|
||||
mysql.query(SET_LAST_SYNC_TIME_SQL, Number(time), callback);
|
||||
};
|
||||
|
||||
var SET_LAST_EXIST_SYNC_TIME_SQL = 'UPDATE total SET last_exist_sync_time=? WHERE name="total";';
|
||||
var SET_LAST_EXIST_SYNC_TIME_SQL = multiline(function () {;/*
|
||||
UPDATE
|
||||
total
|
||||
SET
|
||||
last_exist_sync_time=?
|
||||
WHERE
|
||||
name="total";
|
||||
*/});
|
||||
exports.setLastExistSyncTime = function (time, callback) {
|
||||
mysql.query(SET_LAST_EXIST_SYNC_TIME_SQL, Number(time), callback);
|
||||
};
|
||||
|
||||
var UPDATE_SYNC_STATUS_SQL = 'UPDATE total SET sync_status = ? WHERE name="total";';
|
||||
var UPDATE_SYNC_STATUS_SQL = multiline(function () {;/*
|
||||
UPDATE
|
||||
total
|
||||
SET
|
||||
sync_status=?
|
||||
WHERE
|
||||
name="total";
|
||||
*/});
|
||||
exports.updateSyncStatus = function (status, callback) {
|
||||
mysql.query(UPDATE_SYNC_STATUS_SQL, [status], callback);
|
||||
};
|
||||
|
||||
var UPDATE_SYNC_NUM_SQL = 'UPDATE total SET sync_status = ?, need_sync_num = ?, \
|
||||
success_sync_num = ?, fail_sync_num = ?, left_sync_num = ?, last_sync_module=? \
|
||||
WHERE name="total";';
|
||||
var UPDATE_SYNC_NUM_SQL = multiline(function () {;/*
|
||||
UPDATE
|
||||
total
|
||||
SET
|
||||
?
|
||||
WHERE
|
||||
name="total";
|
||||
*/});
|
||||
exports.updateSyncNum = function (params, callback) {
|
||||
var query = [
|
||||
params.syncStatus, params.need || 0,
|
||||
params.success || 0, params.fail || 0, params.left || 0,
|
||||
params.lastSyncModule,
|
||||
];
|
||||
mysql.query(UPDATE_SYNC_NUM_SQL, query, callback);
|
||||
var arg = {
|
||||
sync_status: params.syncStatus,
|
||||
need_sync_num: params.need || 0,
|
||||
success_sync_num: params.success || 0,
|
||||
fail_sync_num: params.fail || 0,
|
||||
left_sync_num: params.left || 0,
|
||||
last_sync_module: params.lastSyncModule
|
||||
};
|
||||
|
||||
mysql.query(UPDATE_SYNC_NUM_SQL, [arg], callback);
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
183
proxy/user.js
183
proxy/user.js
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
/* jshint -W032 */
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
@@ -18,49 +19,13 @@ var thunkify = require('thunkify-wrap');
|
||||
var utility = require('utility');
|
||||
var config = require('../config');
|
||||
var mysql = require('../common/mysql');
|
||||
var crypto = require('crypto');
|
||||
|
||||
var COLUMNS = 'id, rev, name, email, salt, password_sha, ip, roles, gmt_create, gmt_modified';
|
||||
var SELECT_USER_SQL = 'SELECT ' + COLUMNS + ' FROM user WHERE name=?;';
|
||||
|
||||
function sha1(s) {
|
||||
return crypto.createHash("sha1").update(s).digest("hex");
|
||||
}
|
||||
var multiline = require('multiline');
|
||||
|
||||
function passwordSha(password, salt) {
|
||||
return sha1(password + salt);
|
||||
return utility.sha1(password + salt);
|
||||
}
|
||||
|
||||
exports.get = function (name, callback) {
|
||||
mysql.queryOne(SELECT_USER_SQL, [name], function (err, row) {
|
||||
if (row) {
|
||||
try {
|
||||
row.roles = JSON.parse(row.roles);
|
||||
} catch (e) {
|
||||
row.roles = [];
|
||||
}
|
||||
}
|
||||
callback(err, row);
|
||||
});
|
||||
};
|
||||
|
||||
exports.auth = function (name, password, callback) {
|
||||
exports.get(name, function (err, row) {
|
||||
if (err || !row) {
|
||||
return callback(err, row);
|
||||
}
|
||||
|
||||
var sha = passwordSha(password, row.salt);
|
||||
if (row.password_sha !== sha) {
|
||||
row = null;
|
||||
}
|
||||
callback(null, row);
|
||||
});
|
||||
};
|
||||
|
||||
var INSERT_USER_SQL = 'INSERT INTO user(rev, name, email, salt, password_sha, ip, roles, gmt_create, gmt_modified) \
|
||||
VALUES(?, ?, ?, ?, ?, ?, ?, now(), now())';
|
||||
|
||||
var INSERT_USER_SQL = 'INSERT INTO user SET ?';
|
||||
exports.add = function (user, callback) {
|
||||
var roles = user.roles || [];
|
||||
try {
|
||||
@@ -69,15 +34,34 @@ exports.add = function (user, callback) {
|
||||
roles = '[]';
|
||||
}
|
||||
var rev = '1-' + utility.md5(JSON.stringify(user));
|
||||
var values = [rev, user.name, user.email, user.salt, user.password_sha, user.ip, roles];
|
||||
mysql.query(INSERT_USER_SQL, values, function (err) {
|
||||
|
||||
var now = new Date();
|
||||
|
||||
var arg = {
|
||||
rev: rev,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
salt: user.salt,
|
||||
password_sha: user.password_sha,
|
||||
ip: user.ip,
|
||||
roles: roles,
|
||||
gmt_create: now,
|
||||
gmt_modified: now
|
||||
};
|
||||
|
||||
mysql.query(INSERT_USER_SQL, [arg], function (err) {
|
||||
callback(err, {rev: rev});
|
||||
});
|
||||
};
|
||||
|
||||
var UPDATE_USER_SQL = 'UPDATE user SET rev=?, email=?, salt=?, password_sha=?, ip=?, roles=?, gmt_modified=now() \
|
||||
WHERE name=? AND rev=?;';
|
||||
|
||||
var UPDATE_USER_SQL = multiline(function () {;/*
|
||||
UPDATE
|
||||
user
|
||||
SET
|
||||
?
|
||||
WHERE
|
||||
name=? AND rev=?;
|
||||
*/});
|
||||
exports.update = function (user, callback) {
|
||||
var rev = user.rev || user._rev;
|
||||
var revNo = Number(rev.split('-', 1));
|
||||
@@ -96,14 +80,117 @@ exports.update = function (user, callback) {
|
||||
roles = '[]';
|
||||
}
|
||||
|
||||
var values = [newRev, user.email, user.salt, user.password_sha, user.ip, roles, user.name, rev];
|
||||
mysql.query(UPDATE_USER_SQL, values, function (err, data) {
|
||||
if (err || !data.affectedRows) {
|
||||
var arg = {
|
||||
rev: newRev,
|
||||
email: user.email,
|
||||
salt: user.salt,
|
||||
password_sha: user.password_sha,
|
||||
ip: user.ip,
|
||||
roles: roles,
|
||||
gmt_modified: new Date()
|
||||
};
|
||||
|
||||
mysql.query(UPDATE_USER_SQL, [arg, user.name, rev], function (err, data) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, {rev: newRev});
|
||||
callback(null, {rev: newRev, result: data});
|
||||
});
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
exports.passwordSha = passwordSha;
|
||||
|
||||
var SELECT_USER_SQL = 'SELECT \
|
||||
id, rev, name, email, salt, password_sha, ip, \
|
||||
roles, json, npm_user, gmt_create, gmt_modified \
|
||||
FROM \
|
||||
user \
|
||||
WHERE \
|
||||
name=?;';
|
||||
exports.get = function* (name) {
|
||||
var row = yield mysql.queryOne(SELECT_USER_SQL, [name]);
|
||||
if (row) {
|
||||
try {
|
||||
row.roles = row.roles ? JSON.parse(row.roles) : [];
|
||||
} catch (e) {
|
||||
row.roles = [];
|
||||
}
|
||||
try {
|
||||
row.json = row.json ? JSON.parse(row.json) : null;
|
||||
} catch (e) {
|
||||
row.json = null;
|
||||
}
|
||||
}
|
||||
return row;
|
||||
};
|
||||
|
||||
exports.auth = function* (name, password) {
|
||||
var row = yield* exports.get(name);
|
||||
if (row) {
|
||||
var sha = passwordSha(password, row.salt);
|
||||
if (row.password_sha !== sha) {
|
||||
row = null;
|
||||
}
|
||||
}
|
||||
return row;
|
||||
};
|
||||
|
||||
exports.saveNpmUser = function* (user) {
|
||||
var sql = 'SELECT id, json FROM user WHERE name=?;';
|
||||
var row = yield mysql.queryOne(sql, [user.name]);
|
||||
if (!row) {
|
||||
sql = 'INSERT INTO user(npm_user, json, rev, name, email, salt, password_sha, ip, gmt_create, gmt_modified) \
|
||||
VALUES(1, ?, ?, ?, ?, "0", "0", "0", now(), now());';
|
||||
yield mysql.query(sql, [JSON.stringify(user), user._rev, user.name, user.email]);
|
||||
} else {
|
||||
sql = 'UPDATE user SET json=?, rev=? WHERE id=?;';
|
||||
yield mysql.query(sql, [JSON.stringify(user), user._rev, row.id]);
|
||||
}
|
||||
};
|
||||
|
||||
exports.saveCustomUser = function* (data) {
|
||||
var sql = 'SELECT id, json FROM user WHERE name=?;';
|
||||
var row = yield mysql.queryOne(sql, [data.user.login]);
|
||||
var salt = data.salt || '0';
|
||||
var password_sha = data.password_sha || '0';
|
||||
var ip = data.ip || '0';
|
||||
var rev = rev || '1-' + data.user.login;
|
||||
var json = JSON.stringify(data.user);
|
||||
if (!row) {
|
||||
sql = 'INSERT INTO user(npm_user, json, rev, name, email, salt, password_sha, ip, gmt_create, gmt_modified) \
|
||||
VALUES(2, ?, ?, ?, ?, ?, ?, ?, now(), now());';
|
||||
yield mysql.query(sql, [
|
||||
json, rev, data.user.login, data.user.email,
|
||||
salt, password_sha, ip
|
||||
]);
|
||||
} else {
|
||||
sql = 'UPDATE user SET json=?, rev=?, salt=?, password_sha=?, ip=? WHERE id=?;';
|
||||
yield mysql.query(sql, [
|
||||
json, rev,
|
||||
salt, password_sha, ip,
|
||||
row.id
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
var LIST_BY_NAMES_SQL = 'SELECT \
|
||||
id, name, email, json \
|
||||
FROM \
|
||||
user \
|
||||
WHERE \
|
||||
name in (?);';
|
||||
exports.listByNames = function* (names) {
|
||||
if (names.length === 0) {
|
||||
return [];
|
||||
}
|
||||
return yield mysql.query(LIST_BY_NAMES_SQL, [names]);
|
||||
};
|
||||
|
||||
var SEARCH_SQL = 'SELECT id, name, email, json FROM user WHERE name LIKE ? LIMIT ?;';
|
||||
exports.search = function* (query, options) {
|
||||
var limit = options.limit;
|
||||
query = query + '%';
|
||||
return yield mysql.query(SEARCH_SQL, [query, limit]);
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var middlewares = require('koa-middlewares');
|
||||
var limit = require('../middleware/limit');
|
||||
var login = require('../middleware/login');
|
||||
var publishable = require('../middleware/publishable');
|
||||
var syncByInstall = require('../middleware/sync_by_install');
|
||||
@@ -25,7 +25,15 @@ var user = require('../controllers/registry/user');
|
||||
var sync = require('../controllers/sync');
|
||||
|
||||
function routes(app) {
|
||||
app.get('/', middlewares.jsonp(), total.show);
|
||||
|
||||
function* jsonp(next) {
|
||||
yield* next;
|
||||
if (this.body) {
|
||||
this.jsonp = this.body;
|
||||
}
|
||||
}
|
||||
|
||||
app.get('/', jsonp, total.show);
|
||||
|
||||
//before /:name/:version
|
||||
//get all modules, for npm search
|
||||
@@ -35,28 +43,37 @@ function routes(app) {
|
||||
app.get('/-/short', mod.listAllModuleNames);
|
||||
|
||||
// module
|
||||
// scope package: params: [$name]
|
||||
app.get(/^\/(@[\w\-\.]+\/[\w\-\.]+)$/, syncByInstall, mod.show);
|
||||
// scope package: params: [$name, $version]
|
||||
app.get(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/([\w\.\-]+)$/, syncByInstall, mod.get);
|
||||
|
||||
app.get('/:name', syncByInstall, mod.show);
|
||||
app.get('/:name/:version', syncByInstall, mod.get);
|
||||
// try to add module
|
||||
app.put('/:name', login, publishable, mod.add);
|
||||
app.put(/^\/(@[\w\-\.]+\/[\w\-\.]+)$/, login, publishable, mod.addPackageAndDist);
|
||||
app.put('/:name', login, publishable, mod.addPackageAndDist);
|
||||
|
||||
// sync from source npm
|
||||
app.put('/:name/sync', sync.sync);
|
||||
app.get('/:name/sync/log/:id', sync.getSyncLog);
|
||||
|
||||
app.get('/:name/download/:filename', mod.download);
|
||||
app.put(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/([\w\-\.]+)$/, login, mod.updateTag);
|
||||
app.put('/:name/:tag', login, mod.updateTag);
|
||||
|
||||
// need limit by ip
|
||||
app.get(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/download\/(@[\w\-\.]+\/[\w\-\.]+)$/, limit, mod.download);
|
||||
app.get('/:name/download/:filename', limit, mod.download);
|
||||
|
||||
// put tarball
|
||||
// https://registry.npmjs.org/cnpmjs.org/-/cnpmjs.org-0.0.0.tgz/-rev/1-c85bc65e8d2470cc4d82b8f40da65b8e
|
||||
app.put('/:name/-/:filename/-rev/:rev', login, publishable, mod.upload);
|
||||
// delete tarball
|
||||
app.delete(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/download\/(@[\w\-\.]+\/[\w\-\.]+)\/\-rev\/([\w\-\.]+)$/,
|
||||
login, publishable, mod.removeTar);
|
||||
app.delete('/:name/download/:filename/-rev/:rev', login, publishable, mod.removeTar);
|
||||
|
||||
// put package.json to module
|
||||
app.put('/:name/:version/-tag/latest', login, publishable, mod.updateLatest);
|
||||
|
||||
// update module, unpublish will PUT this
|
||||
app.put('/:name/-rev/:rev', login, publishable, mod.removeWithVersions);
|
||||
app.put(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/\-rev\/([\w\-\.]+)$/, login, publishable, mod.updateOrRemove);
|
||||
app.delete(/^\/(@[\w\-\.]+\/[\w\-\.]+)\/\-rev\/([\w\-\.]+)$/, login, publishable, mod.removeAll);
|
||||
app.put('/:name/-rev/:rev', login, publishable, mod.updateOrRemove);
|
||||
app.delete('/:name/-rev/:rev', login, publishable, mod.removeAll);
|
||||
|
||||
// try to create a new user
|
||||
@@ -64,8 +81,6 @@ function routes(app) {
|
||||
app.put('/-/user/org.couchdb.user::name', user.add);
|
||||
app.get('/-/user/org.couchdb.user::name', user.show);
|
||||
app.put('/-/user/org.couchdb.user::name/-rev/:rev', login, user.update);
|
||||
// _session
|
||||
app.post('/_session', user.authSession);
|
||||
}
|
||||
|
||||
module.exports = routes;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - routes/web.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -19,21 +19,38 @@ var pkg = require('../controllers/web/package');
|
||||
var user = require('../controllers/web/user');
|
||||
var sync = require('../controllers/sync');
|
||||
var total = require('../controllers/total');
|
||||
var dist = require('../controllers/web/dist');
|
||||
|
||||
function routes(app) {
|
||||
app.get('/total', total.show);
|
||||
|
||||
// scope package without version
|
||||
app.get(/\/package\/(@[\w\-\.]+\/[\w\-\.]+)$/, pkg.display);
|
||||
// scope package with version
|
||||
app.get(/\/package\/(@[\w\-\.]+\/[\w\-\.]+)\/([\w\d\.]+)$/, pkg.display);
|
||||
app.get('/package/:name', pkg.display);
|
||||
app.get('/package/:name/:version', pkg.display);
|
||||
|
||||
app.get('/privates', pkg.listPrivates);
|
||||
|
||||
app.get(/\/browse\/keyword\/(@[\w\-\.]+\/[\w\-\.]+)$/, pkg.search);
|
||||
app.get('/browse/keyword/:word', pkg.search);
|
||||
|
||||
app.get('/~:name', user.display);
|
||||
|
||||
app.get('/sync/:name', pkg.displaySync);
|
||||
|
||||
app.put('/sync/:name', sync.sync);
|
||||
|
||||
// params: [$name, $id]
|
||||
app.get(/\/sync\/(@[\w\-\.]+\/[\w\-\.]+)\/log\/(\d+)$/, sync.getSyncLog);
|
||||
app.get('/sync/:name/log/:id', sync.getSyncLog);
|
||||
|
||||
app.get('/sync', pkg.displaySync);
|
||||
|
||||
app.get('/_list/search/search', pkg.rangeSearch);
|
||||
|
||||
app.get(/^\/dist(\/.*)?/, dist.list);
|
||||
}
|
||||
|
||||
module.exports = routes;
|
||||
|
||||
@@ -18,29 +18,29 @@
|
||||
var koa = require('koa');
|
||||
var app = module.exports = koa();
|
||||
var http = require('http');
|
||||
var forward = require('forward');
|
||||
var path = require('path');
|
||||
var middlewares = require('koa-middlewares');
|
||||
var routes = require('../routes/registry');
|
||||
var logger = require('../common/logger');
|
||||
var config = require('../config');
|
||||
var session = require('../common/session');
|
||||
var auth = require('../middleware/auth');
|
||||
var staticCache = require('../middleware/static');
|
||||
var notFound = require('../middleware/registry_not_found');
|
||||
var rootdir = path.dirname(__dirname);
|
||||
|
||||
middlewares.jsonp(app);
|
||||
app.use(middlewares.rt({headerName: 'X-ReadTime'}));
|
||||
|
||||
app.use(middlewares.rewrite('/favicon.ico', '/public/favicon.ico'));
|
||||
app.use(middlewares.rewrite('/favicon.ico', '/favicon.png'));
|
||||
app.use(staticCache);
|
||||
|
||||
app.keys = ['todokey', config.sessionSecret];
|
||||
app.outputErrors = true;
|
||||
app.use(session);
|
||||
app.proxy = true;
|
||||
app.use(middlewares.bodyParser({jsonLimit: config.jsonLimit}));
|
||||
app.use(auth());
|
||||
app.use(notFound);
|
||||
|
||||
app.use(middlewares.fresh());
|
||||
if (config.enableCompress) {
|
||||
app.use(middlewares.compress({threshold: 150}));
|
||||
}
|
||||
app.use(middlewares.conditional());
|
||||
app.use(middlewares.etag());
|
||||
|
||||
/**
|
||||
@@ -55,6 +55,7 @@ routes(app);
|
||||
*/
|
||||
|
||||
app.on('error', function (err, ctx) {
|
||||
// console.log(err.stack)
|
||||
err.url = err.url || ctx.request.url;
|
||||
logger.error(err);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - servers/web.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -21,9 +21,10 @@ var fs = require('fs');
|
||||
var koa = require('koa');
|
||||
var middlewares = require('koa-middlewares');
|
||||
var markdown = require('koa-markdown');
|
||||
var session = require('../common/session');
|
||||
var opensearch = require('../middleware/opensearch');
|
||||
var notFound = require('../middleware/web_not_found');
|
||||
var staticCache = require('../middleware/static');
|
||||
var auth = require('../middleware/auth');
|
||||
var routes = require('../routes/web');
|
||||
var logger = require('../common/logger');
|
||||
var config = require('../config');
|
||||
@@ -33,18 +34,23 @@ var app = koa();
|
||||
var rootdir = path.dirname(__dirname);
|
||||
|
||||
app.use(middlewares.rt({headerName: 'X-ReadTime'}));
|
||||
app.use(middlewares.staticCache(path.join(__dirname, '..', 'public'), {
|
||||
buffer: !config.debug,
|
||||
maxAge: config.debug ? 0 : 60 * 60 * 24 * 7,
|
||||
dir: path.join(rootdir, 'public')
|
||||
}));
|
||||
app.use(middlewares.rewrite('/favicon.ico', '/favicon.png'));
|
||||
app.use(staticCache);
|
||||
|
||||
app.use(opensearch);
|
||||
app.keys = ['todokey', config.sessionSecret];
|
||||
app.outputErrors = true;
|
||||
app.use(session);
|
||||
app.proxy = true;
|
||||
app.use(middlewares.bodyParser());
|
||||
app.use(auth());
|
||||
app.use(notFound);
|
||||
|
||||
if (config.enableCompress) {
|
||||
app.use(middlewares.compress({threshold: 150}));
|
||||
}
|
||||
|
||||
app.use(middlewares.conditional());
|
||||
app.use(middlewares.etag());
|
||||
|
||||
var viewDir = path.join(rootdir, 'view', 'web');
|
||||
var docDir = path.join(rootdir, 'docs', 'web');
|
||||
|
||||
@@ -55,20 +61,30 @@ var layout = fs.readFileSync(path.join(viewDir, 'layout.html'), 'utf8')
|
||||
.replace('{{logoURL}}', config.logoURL);
|
||||
fs.writeFileSync(layoutFile, layout);
|
||||
|
||||
// custom web readme home page support
|
||||
var readmeFile = path.join(docDir, '_readme.md');
|
||||
var readmeContent;
|
||||
if (config.customReadmeFile) {
|
||||
readmeContent = fs.readFileSync(config.customReadmeFile, 'utf8');
|
||||
} else {
|
||||
readmeContent = fs.readFileSync(path.join(docDir, 'readme.md'), 'utf8');
|
||||
}
|
||||
fs.writeFileSync(readmeFile, readmeContent);
|
||||
|
||||
app.use(markdown({
|
||||
baseUrl: '/',
|
||||
root: docDir,
|
||||
layout: layoutFile,
|
||||
titleHolder: '<%- locals.title %>',
|
||||
bodyHolder: '<%- locals.body %>',
|
||||
indexName: 'readme'
|
||||
indexName: '_readme'
|
||||
}));
|
||||
|
||||
var locals = {
|
||||
config: config
|
||||
};
|
||||
|
||||
middlewares.render(app, {
|
||||
middlewares.ejs(app, {
|
||||
root: viewDir,
|
||||
viewExt: 'html',
|
||||
layout: '_layout',
|
||||
@@ -89,6 +105,7 @@ routes(app);
|
||||
|
||||
app.on('error', function (err, ctx) {
|
||||
err.url = err.url || ctx.request.url;
|
||||
console.log(err.stack);
|
||||
logger.error(err);
|
||||
});
|
||||
|
||||
|
||||
139
services/default_user_service.js
Normal file
139
services/default_user_service.js
Normal file
@@ -0,0 +1,139 @@
|
||||
/**!
|
||||
* cnpmjs.org - services/default_user_service.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var gravatar = require('gravatar');
|
||||
var User = require('../proxy/user');
|
||||
var isAdmin = require('../lib/common').isAdmin;
|
||||
var config = require('../config');
|
||||
|
||||
// User: https://github.com/cnpm/cnpmjs.org/wiki/Use-Your-Own-User-Authorization#user-data-structure
|
||||
// {
|
||||
// "login": "fengmk2",
|
||||
// "email": "fengmk2@gmail.com",
|
||||
// "name": "Yuan Feng",
|
||||
// "html_url": "http://fengmk2.github.com",
|
||||
// "avatar_url": "https://avatars3.githubusercontent.com/u/156269?s=460",
|
||||
// "im_url": "",
|
||||
// "site_admin": false,
|
||||
// "scopes": ["@org1", "@org2"]
|
||||
// }
|
||||
|
||||
module.exports = DefaultUserService;
|
||||
|
||||
function convertToUser(row) {
|
||||
var user = {
|
||||
login: row.name,
|
||||
email: row.email,
|
||||
name: row.name,
|
||||
html_url: 'http://cnpmjs.org/~' + row.name,
|
||||
avatar_url: '',
|
||||
im_url: '',
|
||||
site_admin: isAdmin(row.name),
|
||||
scopes: config.scopes
|
||||
};
|
||||
if (row.json) {
|
||||
var data = row.json;
|
||||
if (data.login) {
|
||||
// custom user
|
||||
user = data;
|
||||
} else {
|
||||
// npm user
|
||||
if (data.avatar) {
|
||||
user.avatar_url = data.avatar;
|
||||
}
|
||||
if (data.fullname) {
|
||||
user.name = data.fullname;
|
||||
}
|
||||
if (data.homepage) {
|
||||
user.html_url = data.homepage;
|
||||
}
|
||||
if (data.twitter) {
|
||||
user.im_url = 'https://twitter.com/' + data.twitter;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!user.avatar_url) {
|
||||
user.avatar_url = gravatar.url(user.email, {s: '50', d: 'retro'}, true);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
function DefaultUserService() {}
|
||||
|
||||
var proto = DefaultUserService.prototype;
|
||||
|
||||
/**
|
||||
* Auth user with login name and password
|
||||
* @param {String} login login name
|
||||
* @param {String} password login password
|
||||
* @return {User}
|
||||
*/
|
||||
proto.auth = function* (login, password) {
|
||||
var row = yield* User.auth(login, password);
|
||||
if (!row) {
|
||||
return null;
|
||||
}
|
||||
return convertToUser(row);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get user by login name
|
||||
* @param {String} login login name
|
||||
* @return {User}
|
||||
*/
|
||||
proto.get = function* (login) {
|
||||
var row = yield* User.get(login);
|
||||
if (!row) {
|
||||
return null;
|
||||
}
|
||||
return convertToUser(row);
|
||||
};
|
||||
|
||||
/**
|
||||
* List users
|
||||
* @param {Array<String>} logins login names
|
||||
* @return {Array<User>}
|
||||
*/
|
||||
proto.list = function* (logins) {
|
||||
var rows = yield* User.listByNames(logins);
|
||||
var users = [];
|
||||
rows.forEach(function (row) {
|
||||
users.push(convertToUser(row));
|
||||
});
|
||||
return users;
|
||||
};
|
||||
|
||||
/**
|
||||
* Search users
|
||||
* @param {String} query query keyword
|
||||
* @param {Object} [options] optional query params
|
||||
* - {Number} limit match users count, default is `20`
|
||||
* @return {Array<User>}
|
||||
*/
|
||||
proto.search = function* (query, options) {
|
||||
options = options || {};
|
||||
options.limit = parseInt(options.limit);
|
||||
if (!options.limit || options.limit < 0) {
|
||||
options.limit = 20;
|
||||
}
|
||||
|
||||
var rows = yield* User.search(query, options);
|
||||
var users = [];
|
||||
rows.forEach(function (row) {
|
||||
users.push(convertToUser(row));
|
||||
});
|
||||
return users;
|
||||
};
|
||||
83
services/package.js
Normal file
83
services/package.js
Normal file
@@ -0,0 +1,83 @@
|
||||
/**!
|
||||
* cnpmjs.org - services/package.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Module = require('../proxy/module');
|
||||
var ModuleMaintainer = require('../proxy/module_maintainer');
|
||||
var User = require('../proxy/user');
|
||||
|
||||
exports.listMaintainers = function* (name) {
|
||||
var names = yield* ModuleMaintainer.get(name);
|
||||
if (names.length === 0) {
|
||||
return names;
|
||||
}
|
||||
var users = yield* User.listByNames(names);
|
||||
return users.map(function (user) {
|
||||
return {
|
||||
name: user.name,
|
||||
email: user.email
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
exports.listMaintainerNamesOnly = function* (name) {
|
||||
return yield* ModuleMaintainer.get(name);
|
||||
};
|
||||
|
||||
exports.addMaintainers = function* (name, usernames) {
|
||||
return yield* ModuleMaintainer.addMulti(name, usernames);
|
||||
};
|
||||
|
||||
exports.updateMaintainers = function* (name, usernames) {
|
||||
var rs = yield [
|
||||
ModuleMaintainer.update(name, usernames),
|
||||
Module.updateLastModified(name),
|
||||
];
|
||||
return rs[0];
|
||||
};
|
||||
|
||||
exports.removeAllMaintainers = function* (name) {
|
||||
return yield* ModuleMaintainer.removeAll(name);
|
||||
};
|
||||
|
||||
exports.isMaintainer = function* (name, username) {
|
||||
var rs = yield [
|
||||
ModuleMaintainer.get(name),
|
||||
Module.getLatest(name)
|
||||
];
|
||||
var maintainers = rs[0];
|
||||
var latestMod = rs[1];
|
||||
|
||||
if (latestMod && !latestMod.package._publish_on_cnpm) {
|
||||
// no one can update public package maintainers
|
||||
// public package only sync from source npm registry
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maintainers.length === 0) {
|
||||
// if not found maintainers, try to get from latest module package info
|
||||
var ms = latestMod && latestMod.package && latestMod.package.maintainers;
|
||||
if (ms && ms.length > 0) {
|
||||
maintainers = ms.map(function (user) {
|
||||
return user.name;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (maintainers.length === 0) {
|
||||
// no maintainers, meaning this module is free for everyone
|
||||
return true;
|
||||
}
|
||||
return maintainers.indexOf(username) >= 0;
|
||||
};
|
||||
56
services/user.js
Normal file
56
services/user.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/**!
|
||||
* cnpmjs.org - services/user.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var config = require('../config');
|
||||
if (!config.userService) {
|
||||
var DefaultUserService = require('./default_user_service');
|
||||
config.userService = new DefaultUserService();
|
||||
config.customUserService = false;
|
||||
} else {
|
||||
config.customUserService = true;
|
||||
}
|
||||
config.scopes = config.scopes || [];
|
||||
|
||||
function convertUser(user) {
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
user.scopes = user.scopes || [];
|
||||
if (user.scopes.length === 0 && config.scopes.length > 0) {
|
||||
user.scopes = config.scopes.slice();
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
exports.auth = function* (login, password) {
|
||||
var user = yield* config.userService.auth(login, password);
|
||||
return convertUser(user);
|
||||
};
|
||||
|
||||
exports.get = function* (login) {
|
||||
var user = yield* config.userService.get(login);
|
||||
return convertUser(user);
|
||||
};
|
||||
|
||||
exports.list = function* (logins) {
|
||||
var users = yield* config.userService.list(logins);
|
||||
return users.map(convertUser);
|
||||
};
|
||||
|
||||
exports.search = function* (query, options) {
|
||||
var users = yield* config.userService.search(query, options);
|
||||
return users.map(convertUser);
|
||||
};
|
||||
@@ -14,16 +14,18 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var config = require('../config');
|
||||
var debug = require('debug')('cnpmjs.org:sync:index');
|
||||
var co = require('co');
|
||||
var ms = require('ms');
|
||||
var mail = require('../common/mail');
|
||||
var util = require('util');
|
||||
var utility = require('utility');
|
||||
var debug = require('debug')('cnpmjs.org:sync:index');
|
||||
var config = require('../config');
|
||||
var mail = require('../common/mail');
|
||||
var Total = require('../proxy/total');
|
||||
var logger = require('../common/logger');
|
||||
var syncDistWorker = require('./sync_dist');
|
||||
|
||||
var sync;
|
||||
var sync = null;
|
||||
|
||||
switch (config.syncModel) {
|
||||
case 'all':
|
||||
@@ -34,32 +36,72 @@ case 'exist':
|
||||
break;
|
||||
}
|
||||
|
||||
if (!sync && config.enableCluster) {
|
||||
console.log('[%s] [sync_worker:%s] no need to sync, exit now', Date(), process.pid);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log('[%s] [sync_worker:%s] syncing with %s mode',
|
||||
Date(), process.pid, config.syncModel);
|
||||
|
||||
//set sync_status = 0 at first
|
||||
Total.updateSyncStatus(0, utility.noop);
|
||||
|
||||
// the same time only sync once
|
||||
var syncing = false;
|
||||
|
||||
function handleSync() {
|
||||
var handleSync = co(function *() {
|
||||
debug('mode: %s, syncing: %s', config.syncModel, syncing);
|
||||
// check sync every one 30 minutes
|
||||
if (!syncing) {
|
||||
syncing = true;
|
||||
debug('start syncing');
|
||||
sync(function (err, data) {
|
||||
if (config.debug) {
|
||||
console.log(err, data);
|
||||
} else {
|
||||
sendMailToAdmin(err, data, new Date());
|
||||
}
|
||||
syncing = false;
|
||||
});
|
||||
var data;
|
||||
var error;
|
||||
try {
|
||||
var data = yield *sync();
|
||||
} catch (err) {
|
||||
error = err;
|
||||
error.message += ' (sync package error)';
|
||||
logger.syncError(error);
|
||||
}
|
||||
data && logger.syncInfo(data);
|
||||
if (!config.debug) {
|
||||
sendMailToAdmin(error, data, new Date());
|
||||
}
|
||||
syncing = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (sync) {
|
||||
handleSync();
|
||||
setInterval(handleSync, ms('30m'));
|
||||
setInterval(handleSync, ms(config.syncInterval));
|
||||
}
|
||||
|
||||
var syncingDist = false;
|
||||
var syncDist = co(function* syncDist() {
|
||||
if (syncingDist) {
|
||||
return;
|
||||
}
|
||||
syncingDist = true;
|
||||
logger.syncInfo('Start syncing dist...');
|
||||
try {
|
||||
yield* syncDistWorker();
|
||||
yield* syncDistWorker.syncPhantomjsDir();
|
||||
} catch (err) {
|
||||
err.message += ' (sync dist error)';
|
||||
logger.syncError(err);
|
||||
if (config.noticeSyncDistError) {
|
||||
sendMailToAdmin(err, null, new Date());
|
||||
}
|
||||
}
|
||||
syncingDist = false;
|
||||
});
|
||||
|
||||
if (config.syncDist) {
|
||||
syncDist();
|
||||
setInterval(syncDist, ms(config.syncInterval));
|
||||
} else {
|
||||
logger.syncInfo('sync dist disable');
|
||||
}
|
||||
|
||||
function sendMailToAdmin(err, result, syncTime) {
|
||||
@@ -77,7 +119,7 @@ function sendMailToAdmin(err, result, syncTime) {
|
||||
subject = 'Sync Error';
|
||||
type = 'error';
|
||||
html = util.format('Sync packages from official registry failed.\n' +
|
||||
'Start sync time is %s.\nError message is %s.', syncTime, err.stack);
|
||||
'Start sync time is %s.\nError message is %s: %s\n%s.', syncTime, err.name, err.message, err.stack);
|
||||
} else if (result.fails && result.fails.length) {
|
||||
subject = 'Sync Finished But Some Packages Failed';
|
||||
type = 'warn';
|
||||
@@ -85,7 +127,7 @@ function sendMailToAdmin(err, result, syncTime) {
|
||||
'Start sync time is %s.\n %d packges sync failed: %j ...\n %d packages sync successes :%j ...',
|
||||
syncTime, result.fails.length, result.fails.slice(0, 10),
|
||||
result.successes.length, result.successes.slice(0, 10));
|
||||
} else {
|
||||
} else if (result.successes && result.successes.length) {
|
||||
subject = 'Sync Finished';
|
||||
type = 'log';
|
||||
html = util.format('Sync packages from official registry finished.\n' +
|
||||
@@ -93,10 +135,10 @@ function sendMailToAdmin(err, result, syncTime) {
|
||||
syncTime, result.successes.length, result.successes.slice(0, 10));
|
||||
}
|
||||
debug('send email with type: %s, subject: %s, html: %s', type, subject, html);
|
||||
if (type !== 'log') {
|
||||
logger.syncInfo('send email with type: %s, subject: %s, html: %s', type, subject, html);
|
||||
if (type && type !== 'log') {
|
||||
mail[type](to, subject, html, function (err) {
|
||||
if (err) {
|
||||
logger.info('send email with type: %s, subject: %s, html: %s', type, subject, html);
|
||||
logger.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -19,18 +19,20 @@ var utility = require('utility');
|
||||
var Total = require('../proxy/total');
|
||||
|
||||
function Status(options) {
|
||||
this.worker = options.worker;
|
||||
this.need = options.need;
|
||||
this.lastSyncModule = '';
|
||||
this.successes = 0;
|
||||
this.fails = 0;
|
||||
this.left = options.need;
|
||||
}
|
||||
|
||||
Status.prototype.log = function (syncDone) {
|
||||
var params = {
|
||||
syncStatus: syncDone ? 0 : 1,
|
||||
need: this.need,
|
||||
success: this.worker.successes.length,
|
||||
fail: this.worker.fails.length,
|
||||
left: this.worker.names.length,
|
||||
success: this.successes,
|
||||
fail: this.fails,
|
||||
left: this.left,
|
||||
lastSyncModule: this.lastSyncModule,
|
||||
};
|
||||
Total.updateSyncNum(params, utility.noop);
|
||||
@@ -43,19 +45,35 @@ Status.prototype.start = function () {
|
||||
this.started = true;
|
||||
//every 30s log it into mysql
|
||||
this.timer = setInterval(this.log.bind(this), 30000);
|
||||
this.worker.on('success', function (moduleName) {
|
||||
debug('sync [%s] success', moduleName);
|
||||
this.lastSyncModule = moduleName;
|
||||
}.bind(this));
|
||||
this.worker.on('end', function () {
|
||||
this.started = false;
|
||||
this.log(true);
|
||||
clearInterval(this.timer);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Status.init = function (options) {
|
||||
return new Status(options);
|
||||
Status.prototype.stop = function () {
|
||||
this.log(true);
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
this.started = false;
|
||||
};
|
||||
|
||||
Status.init = function (options, worker) {
|
||||
var status = new Status(options);
|
||||
status.start();
|
||||
worker.on('success', function (moduleName) {
|
||||
debug('sync [%s] success', moduleName);
|
||||
status.lastSyncModule = moduleName;
|
||||
status.successes++;
|
||||
status.left--;
|
||||
});
|
||||
worker.on('fail', function () {
|
||||
status.fails++;
|
||||
status.left--;
|
||||
});
|
||||
worker.on('add', function () {
|
||||
status.left++;
|
||||
});
|
||||
|
||||
worker.on('end', function () {
|
||||
status.stop();
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Status;
|
||||
|
||||
149
sync/sync_all.js
149
sync/sync_all.js
@@ -24,6 +24,7 @@ var Npm = require('../proxy/npm');
|
||||
var Total = require('../proxy/total');
|
||||
var SyncModuleWorker = require('../proxy/sync_module_worker');
|
||||
var Module = require('../proxy/module');
|
||||
var thunkify = require('thunkify-wrap');
|
||||
|
||||
function subtract(subtracter, minuend) {
|
||||
subtracter = subtracter || [];
|
||||
@@ -55,109 +56,87 @@ function union(arrOne, arrTwo) {
|
||||
* when sync from official at the first time
|
||||
* get all packages by short and restart from last synced module
|
||||
* @param {String} lastSyncModule
|
||||
* @param {Function} callback
|
||||
*/
|
||||
function getFirstSyncPackages(lastSyncModule, callback) {
|
||||
Npm.getShort(function (err, pkgs) {
|
||||
if (err || !lastSyncModule) {
|
||||
return callback(err, pkgs);
|
||||
}
|
||||
// start from last success
|
||||
var lastIndex = pkgs.indexOf(lastSyncModule);
|
||||
if (lastIndex > 0) {
|
||||
pkgs = pkgs.slice(lastIndex);
|
||||
}
|
||||
return callback(null, pkgs);
|
||||
});
|
||||
function *getFirstSyncPackages(lastSyncModule) {
|
||||
var pkgs = yield Npm.getShort();
|
||||
if (!lastSyncModule) {
|
||||
return pkgs;
|
||||
}
|
||||
// start from last success
|
||||
var lastIndex = pkgs.indexOf(lastSyncModule);
|
||||
if (lastIndex > 0) {
|
||||
return pkgs.slice(lastIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get all the packages that update time > lastSyncTime
|
||||
* @param {Number} lastSyncTime
|
||||
* @param {Function} callback
|
||||
*/
|
||||
function getCommonSyncPackages(lastSyncTime, callback) {
|
||||
Npm.getAllSince(lastSyncTime, function (err, data) {
|
||||
if (err || !data) {
|
||||
return callback(err, []);
|
||||
}
|
||||
delete data._updated;
|
||||
return callback(null, Object.keys(data));
|
||||
});
|
||||
function *getCommonSyncPackages(lastSyncTime) {
|
||||
var data = yield Npm.getAllSince(lastSyncTime);
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
delete data._updated;
|
||||
return Object.keys(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* get all the missing packages
|
||||
* @param {Function} callback
|
||||
*/
|
||||
function getMissPackages(callback) {
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(callback);
|
||||
Npm.getShort(ep.doneLater('allPackages'));
|
||||
Module.listAllModuleNames(ep.doneLater(function (rows) {
|
||||
var existPackages = rows.map(function (row) {
|
||||
return row.name;
|
||||
});
|
||||
ep.emit('existPackages', existPackages);
|
||||
}));
|
||||
ep.all('allPackages', 'existPackages', function (allPackages, existPackages) {
|
||||
callback(null, subtract(allPackages, existPackages));
|
||||
function *getMissPackages(callback) {
|
||||
var r = yield [Npm.getShort(), Module.listAllModuleNames];
|
||||
var allPackages = r[0];
|
||||
var existPackages = r[1].map(function (row) {
|
||||
return row.name;
|
||||
});
|
||||
return subtract(allPackages, existPackages);
|
||||
}
|
||||
|
||||
//only sync not exist once
|
||||
var syncNotExist = false;
|
||||
module.exports = function sync(callback) {
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(callback);
|
||||
module.exports = function *sync() {
|
||||
var syncTime = Date.now();
|
||||
Total.getTotalInfo(ep.doneLater('totalInfo'));
|
||||
var info = yield Total.getTotalInfo();
|
||||
if (!info) {
|
||||
throw new Error('can not found total info');
|
||||
}
|
||||
|
||||
ep.once('totalInfo', function (info) {
|
||||
if (!info) {
|
||||
return callback(new Error('can not found total info'));
|
||||
}
|
||||
debug('Last sync time %s', new Date(info.last_sync_time));
|
||||
// TODO: 记录上次同步的最后一个模块名称
|
||||
if (!info.last_sync_time) {
|
||||
debug('First time sync all packages from official registry');
|
||||
return getFirstSyncPackages(info.last_sync_module, ep.done('syncPackages'));
|
||||
}
|
||||
if (syncNotExist) {
|
||||
getMissPackages(ep.done('missPackages'));
|
||||
syncNotExist = false;
|
||||
} else {
|
||||
ep.emitLater('missPackages', []);
|
||||
}
|
||||
getCommonSyncPackages(info.last_sync_time - ms('10m'), ep.doneLater('newestPackages'));
|
||||
ep.all('missPackages', 'newestPackages', function (missPackages, newestPackages) {
|
||||
ep.emit('syncPackages', union(missPackages, newestPackages));
|
||||
});
|
||||
});
|
||||
var packages;
|
||||
debug('Last sync time %s', new Date(info.last_sync_time));
|
||||
if (!info.last_sync_time) {
|
||||
debug('First time sync all packages from official registry');
|
||||
packages = yield getFirstSyncPackages(info.last_sync_module);
|
||||
} else {
|
||||
packages = yield getCommonSyncPackages(info.last_sync_time - ms('10m'));
|
||||
}
|
||||
|
||||
ep.once('syncPackages', function (packages) {
|
||||
packages = packages || [];
|
||||
debug('Total %d packages to sync', packages.length);
|
||||
var worker = new SyncModuleWorker({
|
||||
username: 'admin',
|
||||
name: packages,
|
||||
noDep: true,
|
||||
concurrency: config.syncConcurrency,
|
||||
});
|
||||
Status.init({
|
||||
worker: worker,
|
||||
need: packages.length
|
||||
}).start();
|
||||
worker.start();
|
||||
worker.once('end', function () {
|
||||
debug('All packages sync done, successes %d, fails %d',
|
||||
worker.successes.length, worker.fails.length);
|
||||
//only when all succss, set last sync time
|
||||
!worker.fails.length && Total.setLastSyncTime(syncTime, utility.noop);
|
||||
callback(null, {
|
||||
successes: worker.successes,
|
||||
fails: worker.fails
|
||||
});
|
||||
});
|
||||
packages = packages || [];
|
||||
if (!packages.length) {
|
||||
debug('no packages need be sync');
|
||||
return;
|
||||
}
|
||||
debug('Total %d packages to sync', packages.length);
|
||||
|
||||
var worker = new SyncModuleWorker({
|
||||
username: 'admin',
|
||||
name: packages,
|
||||
noDep: true,
|
||||
concurrency: config.syncConcurrency,
|
||||
});
|
||||
Status.init({need: packages.length}, worker);
|
||||
worker.start();
|
||||
var end = thunkify.event(worker);
|
||||
yield end();
|
||||
|
||||
debug('All packages sync done, successes %d, fails %d',
|
||||
worker.successes.length, worker.fails.length);
|
||||
//only when all succss, set last sync time
|
||||
if (!worker.fails.length) {
|
||||
Total.setLastSyncTime(syncTime, utility.noop);
|
||||
}
|
||||
return {
|
||||
successes: worker.successes,
|
||||
fails: worker.fails
|
||||
};
|
||||
};
|
||||
|
||||
340
sync/sync_dist.js
Normal file
340
sync/sync_dist.js
Normal file
@@ -0,0 +1,340 @@
|
||||
/**!
|
||||
* cnpmjs.org - sync/sync_dist.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:sync:sync_dist');
|
||||
var fs = require('fs');
|
||||
var urllib = require('co-urllib');
|
||||
var co = require('co');
|
||||
var bytes = require('bytes');
|
||||
var crypto = require('crypto');
|
||||
var utility = require('utility');
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var cheerio = require('cheerio');
|
||||
var urlResolve = require('url').resolve;
|
||||
var common = require('../lib/common');
|
||||
var Dist = require('../proxy/dist');
|
||||
var config = require('../config');
|
||||
var nfs = require('../common/nfs');
|
||||
var logger = require('../common/logger');
|
||||
|
||||
var disturl = config.disturl;
|
||||
var USER_AGENT = 'distsync.cnpmjs.org/' + config.version + ' ' + urllib.USER_AGENT;
|
||||
|
||||
module.exports = sync;
|
||||
|
||||
function* sync(name) {
|
||||
name = name || '/';
|
||||
yield* syncDir(name);
|
||||
}
|
||||
|
||||
function* syncDir(fullname, info) {
|
||||
var news = yield* sync.listdiff(fullname);
|
||||
var files = [];
|
||||
var dirs = [];
|
||||
|
||||
for (var i = 0; i < news.length; i++) {
|
||||
var item = news[i];
|
||||
if (item.type === 'dir') {
|
||||
dirs.push(item);
|
||||
} else {
|
||||
files.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
logger.syncInfo('sync remote:%s got %d new items, %d dirs, %d files to sync',
|
||||
fullname, news.length, dirs.length, files.length);
|
||||
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
yield* syncFile(files[i]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < dirs.length; i++) {
|
||||
var dir = dirs[i];
|
||||
yield* syncDir(dir.parent + dir.name, dir);
|
||||
}
|
||||
|
||||
if (info) {
|
||||
logger.syncInfo('Save dir:%s %j to database', fullname, info);
|
||||
yield* Dist.savedir(info);
|
||||
}
|
||||
|
||||
logger.syncInfo('Sync %s finished, %d dirs, %d files',
|
||||
fullname, dirs.length, files.length);
|
||||
}
|
||||
|
||||
function* syncFile(info) {
|
||||
var name = info.parent + info.name;
|
||||
name = process.pid + name.replace(/\//g, '_'); // make sure no parent dir
|
||||
var isPhantomjsURL = false;
|
||||
var downurl = disturl + info.parent + info.name;
|
||||
if (info.downloadURL) {
|
||||
downurl = info.downloadURL;
|
||||
isPhantomjsURL = true;
|
||||
}
|
||||
var filepath = common.getTarballFilepath(name);
|
||||
var ws = fs.createWriteStream(filepath);
|
||||
|
||||
var options = {
|
||||
writeStream: ws,
|
||||
followRedirect: true,
|
||||
timeout: 6000000, // 100 minutes download
|
||||
headers: {
|
||||
'user-agent': USER_AGENT
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
logger.syncInfo('downloading %s %s to %s, isPhantomjsURL: %s',
|
||||
bytes(info.size), downurl, filepath, isPhantomjsURL);
|
||||
// get tarball
|
||||
var r = yield *urllib.request(downurl, options);
|
||||
var statusCode = r.status || -1;
|
||||
logger.syncInfo('download %s got status %s, headers: %j',
|
||||
downurl, statusCode, r.headers);
|
||||
if (statusCode !== 200) {
|
||||
var err = new Error('Download ' + downurl + ' fail, status: ' + statusCode);
|
||||
err.name = 'DownloadDistFileError';
|
||||
throw err;
|
||||
}
|
||||
|
||||
var shasum = crypto.createHash('sha1');
|
||||
var dataSize = 0;
|
||||
var rs = fs.createReadStream(filepath);
|
||||
rs.on('data', function (data) {
|
||||
shasum.update(data);
|
||||
dataSize += data.length;
|
||||
});
|
||||
var end = thunkify.event(rs);
|
||||
yield end(); // after end event emit
|
||||
|
||||
if (dataSize === 0) {
|
||||
var err = new Error('Download ' + downurl + ' file size is zero');
|
||||
err.name = 'DownloadDistFileZeroSizeError';
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (isPhantomjsURL) {
|
||||
debug('real size: %s, expect size: %s', dataSize, info.size);
|
||||
if (dataSize < info.size) {
|
||||
// phantomjs download page only show `6.7 MB`
|
||||
var err = new Error('Download ' + downurl + ' file size is '
|
||||
+ dataSize + ' not match ' + info.size);
|
||||
err.name = 'DownloadDistFileSizeError';
|
||||
throw err;
|
||||
}
|
||||
info.size = dataSize;
|
||||
} else if (dataSize !== info.size) {
|
||||
var err = new Error('Download ' + downurl + ' file size is '
|
||||
+ dataSize + ' not match ' + info.size);
|
||||
err.name = 'DownloadDistFileSizeError';
|
||||
throw err;
|
||||
}
|
||||
|
||||
shasum = shasum.digest('hex');
|
||||
var args = {
|
||||
key: '/dist' + info.parent + info.name,
|
||||
size: info.size,
|
||||
shasum: shasum,
|
||||
};
|
||||
|
||||
// upload to NFS
|
||||
logger.syncInfo('uploading %s to nfs:%s', filepath, args.key);
|
||||
var result = yield nfs.upload(filepath, args);
|
||||
info.url = result.url || result.key;
|
||||
info.sha1 = shasum;
|
||||
|
||||
logger.syncInfo('upload %s to nfs:%s with size:%d, sha1:%s',
|
||||
args.key, info.url, info.size, info.sha1);
|
||||
} finally {
|
||||
// remove tmp file whatever
|
||||
fs.unlink(filepath, utility.noop);
|
||||
}
|
||||
|
||||
logger.syncInfo('Sync dist file: %j done', info);
|
||||
yield* Dist.savefile(info);
|
||||
}
|
||||
|
||||
// <a href="latest/">latest/</a> 02-May-2014 14:45 -
|
||||
// <a href="node-v0.4.10.tar.gz">node-v0.4.10.tar.gz</a> 26-Aug-2011 16:22 12410018
|
||||
var FILE_RE = /^<a[^>]+>([^<]+)<\/a>\s+(\d+\-\w+\-\d+ \d+\:\d+)\s+([\-\d]+)/;
|
||||
|
||||
function* listdir(fullname) {
|
||||
var url = disturl + fullname;
|
||||
var result = yield* urllib.request(url, {
|
||||
timeout: 60000,
|
||||
});
|
||||
debug('listdir %s got %s, %j', url, result.status, result.headers);
|
||||
var html = result.data && result.data.toString() || '';
|
||||
var lines = html.split('\n');
|
||||
var items = [];
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var m = FILE_RE.exec(lines[i].trim());
|
||||
if (!m) {
|
||||
continue;
|
||||
}
|
||||
var itemName = m[1].replace(/^\/+/, '');
|
||||
if (!itemName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter /nightlies/*
|
||||
if (itemName.indexOf('nightlies/') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
items.push({
|
||||
name: itemName, // 'SHASUMS.txt', 'x64/'
|
||||
date: m[2],
|
||||
size: m[3] === '-' ? '-' : parseInt(m[3]),
|
||||
type: m[3] === '-' ? 'dir' : 'file',
|
||||
parent: fullname, // '/', '/v0.10.28/'
|
||||
});
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
sync.listdiff = function* (fullname) {
|
||||
var items = yield* listdir(fullname);
|
||||
if (items.length === 0) {
|
||||
return items;
|
||||
}
|
||||
var exists = yield* Dist.listdir(fullname);
|
||||
debug('listdiff %s got %s exists items', fullname, exists.length);
|
||||
var map = {};
|
||||
for (var i = 0; i < exists.length; i++) {
|
||||
var item = exists[i];
|
||||
map[item.name] = item;
|
||||
}
|
||||
var news = [];
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var item = items[i];
|
||||
var exist = map[item.name];
|
||||
if (!exist || exist.date !== item.date) {
|
||||
news.push(item);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.size !== '-' && item.size !== exist.size) {
|
||||
news.push(item);
|
||||
continue;
|
||||
}
|
||||
|
||||
debug('skip %s', item.name);
|
||||
}
|
||||
return news;
|
||||
};
|
||||
|
||||
function* syncPhantomjsDir() {
|
||||
var fullname = '/phantomjs/';
|
||||
var files = yield* sync.listPhantomjsDiff(fullname);
|
||||
|
||||
logger.syncInfo('sync remote:%s got %d files to sync',
|
||||
fullname, files.length);
|
||||
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
yield* syncFile(files[i]);
|
||||
}
|
||||
|
||||
logger.syncInfo('SyncPhantomjsDir %s finished, %d files',
|
||||
fullname, files.length);
|
||||
}
|
||||
sync.syncPhantomjsDir = syncPhantomjsDir;
|
||||
|
||||
// <tr class="iterable-item" id="download-301626">
|
||||
// <td class="name"><a class="execute" href="/ariya/phantomjs/downloads/phantomjs-1.9.7-windows.zip">phantomjs-1.9.7-windows.zip</a></td>
|
||||
// <td class="size">6.7 MB</td>
|
||||
// <td class="uploaded-by"><a href="/Vitallium">Vitallium</a></td>
|
||||
// <td class="count">122956</td>
|
||||
// <td class="date">
|
||||
// <div>
|
||||
// <time datetime="2014-01-27T18:29:53.706942" data-title="true">2014-01-27</time>
|
||||
// </div>
|
||||
// </td>
|
||||
// <td class="delete">
|
||||
//
|
||||
// </td>
|
||||
// </tr>
|
||||
|
||||
function* listPhantomjsDir(fullname) {
|
||||
var url = 'https://bitbucket.org/ariya/phantomjs/downloads';
|
||||
var result = yield* urllib.request(url, {
|
||||
timeout: 60000,
|
||||
});
|
||||
debug('listPhantomjsDir %s got %s, %j', url, result.status, result.headers);
|
||||
var html = result.data && result.data.toString() || '';
|
||||
var $ = cheerio.load(html);
|
||||
var items = [];
|
||||
$('tr.iterable-item').each(function (i, el) {
|
||||
var $el = $(this);
|
||||
var $link = $el.find('.name a');
|
||||
var name = $link.text();
|
||||
var downloadURL = $link.attr('href');
|
||||
if (!name || !downloadURL || !/\.(zip|bz2|gz)$/.test(downloadURL)) {
|
||||
return;
|
||||
}
|
||||
downloadURL = urlResolve(url, downloadURL);
|
||||
var size = parseInt(bytes($el.find('.size').text().toLowerCase().replace(/\s/g, '')));
|
||||
if (size > 1024 * 1024) {
|
||||
size -= 1024 * 1024;
|
||||
} else if (size > 1024) {
|
||||
size -= 1024;
|
||||
} else {
|
||||
size -= 10;
|
||||
}
|
||||
var date = $el.find('.date time').text();
|
||||
items.push({
|
||||
name: name, // 'SHASUMS.txt', 'x64/'
|
||||
date: date,
|
||||
size: size,
|
||||
type: 'file',
|
||||
parent: fullname,
|
||||
downloadURL: downloadURL,
|
||||
});
|
||||
});
|
||||
return items;
|
||||
}
|
||||
sync.listPhantomjsDir = listPhantomjsDir;
|
||||
|
||||
sync.listPhantomjsDiff = function* (fullname) {
|
||||
var items = yield* listPhantomjsDir(fullname);
|
||||
if (items.length === 0) {
|
||||
return items;
|
||||
}
|
||||
var exists = yield* Dist.listdir(fullname);
|
||||
debug('listdiff %s got %s exists items', fullname, exists.length);
|
||||
var map = {};
|
||||
for (var i = 0; i < exists.length; i++) {
|
||||
var item = exists[i];
|
||||
map[item.name] = item;
|
||||
}
|
||||
var news = [];
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var item = items[i];
|
||||
var exist = map[item.name];
|
||||
if (!exist || exist.date !== item.date) {
|
||||
news.push(item);
|
||||
continue;
|
||||
}
|
||||
|
||||
// if (item.size !== exist.size) {
|
||||
// news.push(item);
|
||||
// continue;
|
||||
// }
|
||||
|
||||
debug('skip %s', item.name);
|
||||
}
|
||||
return news;
|
||||
};
|
||||
@@ -24,6 +24,7 @@ var debug = require('debug')('cnpmjs.org:sync:sync_hot');
|
||||
var utility = require('utility');
|
||||
var Status = require('./status');
|
||||
var ms = require('ms');
|
||||
var thunkify = require('thunkify-wrap');
|
||||
|
||||
function intersection(arrOne, arrTwo) {
|
||||
arrOne = arrOne || [];
|
||||
@@ -39,71 +40,67 @@ function intersection(arrOne, arrTwo) {
|
||||
return results;
|
||||
}
|
||||
|
||||
module.exports = function sync(callback) {
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(callback);
|
||||
module.exports = function *sync() {
|
||||
var syncTime = Date.now();
|
||||
|
||||
Module.listShort(ep.doneLater(function (packages) {
|
||||
packages = packages.map(function (p) {
|
||||
return p.name;
|
||||
});
|
||||
ep.emit('existPackages', packages);
|
||||
}));
|
||||
Total.getTotalInfo(ep.doneLater('totalInfo'));
|
||||
|
||||
ep.once('totalInfo', function (info) {
|
||||
if (!info) {
|
||||
return callback(new Error('can not found total info'));
|
||||
}
|
||||
if (!info.last_exist_sync_time) {
|
||||
debug('First time sync all packages from official registry');
|
||||
return Npm.getShort(ep.done(function (pkgs) {
|
||||
if (!info.last_sync_module) {
|
||||
return ep.emit('allPackages', pkgs);
|
||||
}
|
||||
// start from last success
|
||||
var lastIndex = pkgs.indexOf(info.last_sync_module);
|
||||
if (lastIndex > 0) {
|
||||
pkgs = pkgs.slice(lastIndex);
|
||||
}
|
||||
ep.emit('allPackages', pkgs);
|
||||
}));
|
||||
}
|
||||
Npm.getAllSince(info.last_exist_sync_time - ms('10m'), ep.done(function (data) {
|
||||
if (!data) {
|
||||
return ep.emit('allPackages', []);
|
||||
}
|
||||
if (data._updated) {
|
||||
syncTime = data._updated;
|
||||
delete data._updated;
|
||||
}
|
||||
|
||||
return ep.emit('allPackages', Object.keys(data));
|
||||
}));
|
||||
var r = yield [Module.listShort(), Total.getTotalInfo()];
|
||||
var existPackages = r[0].map(function (p) {
|
||||
return p.name;
|
||||
});
|
||||
var info = r[1];
|
||||
if (!info) {
|
||||
throw new Error('can not found total info');
|
||||
}
|
||||
|
||||
ep.all('existPackages', 'allPackages', function (existPackages, allPackages) {
|
||||
var packages = intersection(existPackages, allPackages);
|
||||
debug('Total %d packages to sync', packages.length);
|
||||
var worker = new SyncModuleWorker({
|
||||
username: 'admin',
|
||||
name: packages,
|
||||
concurrency: config.syncConcurrency,
|
||||
});
|
||||
Status.init({
|
||||
worker: worker,
|
||||
need: packages.length
|
||||
}).start();
|
||||
worker.start();
|
||||
worker.once('end', function () {
|
||||
debug('All packages sync done, successes %d, fails %d',
|
||||
worker.successes.length, worker.fails.length);
|
||||
Total.setLastExistSyncTime(syncTime, utility.noop);
|
||||
callback(null, {
|
||||
successes: worker.successes,
|
||||
fails: worker.fails
|
||||
});
|
||||
});
|
||||
var allPackages;
|
||||
if (!info.last_exist_sync_time) {
|
||||
debug('First time sync all packages from official registry');
|
||||
var pkgs = yield Npm.getShort();
|
||||
|
||||
if (info.last_sync_module) {
|
||||
// start from last success
|
||||
var lastIndex = pkgs.indexOf(info.last_sync_module);
|
||||
if (lastIndex > 0) {
|
||||
pkgs = pkgs.slice(lastIndex);
|
||||
}
|
||||
}
|
||||
allPackages = pkgs;
|
||||
} else {
|
||||
debug('sync new module from last exist sync time: %s', info.last_sync_time);
|
||||
var data = yield Npm.getAllSince(info.last_exist_sync_time - ms('10m'));
|
||||
if (!data) {
|
||||
allPackages = [];
|
||||
}
|
||||
if (data._updated) {
|
||||
syncTime = data._updated;
|
||||
delete data._updated;
|
||||
}
|
||||
allPackages = Object.keys(data);
|
||||
}
|
||||
|
||||
var packages = intersection(existPackages, allPackages);
|
||||
if (!packages.length) {
|
||||
debug('no packages need be sync');
|
||||
return;
|
||||
}
|
||||
debug('Total %d packages to sync', packages.length);
|
||||
|
||||
var worker = new SyncModuleWorker({
|
||||
username: 'admin',
|
||||
name: packages,
|
||||
concurrency: config.syncConcurrency
|
||||
});
|
||||
Status.init({need: packages.length}, worker);
|
||||
worker.start();
|
||||
var end = thunkify.event(worker);
|
||||
yield end();
|
||||
|
||||
debug('All packages sync done, successes %d, fails %d',
|
||||
worker.successes.length, worker.fails.length);
|
||||
|
||||
Total.setLastExistSyncTime(syncTime, utility.noop);
|
||||
return {
|
||||
successes: worker.successes,
|
||||
fails: worker.fails
|
||||
};
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
105
test/controllers/registry/module/config_private_packages.test.js
Normal file
105
test/controllers/registry/module/config_private_packages.test.js
Normal file
@@ -0,0 +1,105 @@
|
||||
/*!
|
||||
* cnpmjs.org - test/controllers/registry/module/public_mode.test.js
|
||||
* Copyright(c) 2014 dead_horse <dead_horse@qq.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var should = require('should');
|
||||
var request = require('supertest');
|
||||
var mm = require('mm');
|
||||
var config = require('../../../../config');
|
||||
var app = require('../../../../servers/registry');
|
||||
var utils = require('../../../utils');
|
||||
|
||||
describe('controllers/registry/module/config_private_packages.test.js', function () {
|
||||
beforeEach(function () {
|
||||
mm(config, 'enablePrivate', false);
|
||||
mm(config, 'forcePublishWithScope', true);
|
||||
mm(config, 'privatePackages', ['private-package']);
|
||||
});
|
||||
|
||||
after(mm.restore);
|
||||
it('should publish with tgz base64, addPackageAndDist()', function (done) {
|
||||
var pkg = utils.getPackage('private-package', '0.0.1', utils.otherUser);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(201, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'rev');
|
||||
res.body.ok.should.equal(true);
|
||||
pkg = utils.getPackage('private-package', '0.0.1', utils.otherUser);
|
||||
// upload again should 403
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(403, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.eql({
|
||||
error: 'forbidden',
|
||||
reason: 'cannot modify pre-existing version: 0.0.1'
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should other user publish 403', function (done) {
|
||||
var pkg = utils.getPackage('private-package', '0.0.2', utils.secondUser);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.secondUserAuth)
|
||||
.send(pkg)
|
||||
.expect(/forbidden user/)
|
||||
.expect(403, done);
|
||||
});
|
||||
|
||||
it('should admin publish 403', function (done) {
|
||||
var pkg = utils.getPackage('private-package', '0.0.2', utils.admin);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send(pkg)
|
||||
.expect(/forbidden user/)
|
||||
.expect(403, done);
|
||||
});
|
||||
|
||||
it('should add again new maintainers', function (done) {
|
||||
request(app)
|
||||
.put('/private-package/-rev/1')
|
||||
.send({
|
||||
maintainers: [{
|
||||
name: 'cnpmjstest101',
|
||||
email: 'cnpmjstest101@cnpmjs.org'
|
||||
}, {
|
||||
name: 'fengmk2',
|
||||
email: 'fengmk2@cnpmjs.org'
|
||||
}]
|
||||
})
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect(201)
|
||||
.expect('content-type', 'application/json; charset=utf-8', done);
|
||||
});
|
||||
|
||||
it('should remove maintainers', function (done) {
|
||||
request(app)
|
||||
.put('/private-package/-rev/1')
|
||||
.send({
|
||||
maintainers: [{
|
||||
name: 'cnpmjstest101',
|
||||
email: 'cnpmjstest101@cnpmjs.org'
|
||||
}]
|
||||
})
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect(201)
|
||||
.expect('content-type', 'application/json; charset=utf-8', done);
|
||||
});
|
||||
});
|
||||
100
test/controllers/registry/module/maintainer.test.js
Normal file
100
test/controllers/registry/module/maintainer.test.js
Normal file
@@ -0,0 +1,100 @@
|
||||
/**!
|
||||
* cnpmjs.org - test/controllers/registry/module/maintainer.test.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var should = require('should');
|
||||
var request = require('supertest');
|
||||
var mm = require('mm');
|
||||
var config = require('../../../../config');
|
||||
var app = require('../../../../servers/registry');
|
||||
var utils = require('../../../utils');
|
||||
|
||||
describe('controllers/registry/module/maintainer.test.js', function () {
|
||||
var pkgname = '@cnpm/test-package-maintainer';
|
||||
var pkgURL = '/@' + encodeURIComponent(pkgname.substring(1));
|
||||
before(function (done) {
|
||||
app = app.listen(0, function () {
|
||||
// add scope package
|
||||
var pkg = utils.getPackage(pkgname, '0.0.1', utils.admin);
|
||||
|
||||
request(app)
|
||||
.put(pkgURL)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send(pkg)
|
||||
.expect(201, done);
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
mm(config, 'scopes', ['@cnpm', '@cnpmtest']);
|
||||
});
|
||||
|
||||
afterEach(mm.restore);
|
||||
|
||||
it('should add new maintainer without custom user service', function (done) {
|
||||
mm(config, 'customUserService', false);
|
||||
request(app)
|
||||
.put('/@cnpm/test-package-maintainer/-rev/1')
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send({
|
||||
maintainers: [
|
||||
{ name: 'new-maintainer', email: 'new-maintainer@cnpmjs.org' },
|
||||
{ name: utils.admin, email: utils.admin + '@cnpmjs.org' },
|
||||
]
|
||||
})
|
||||
.expect(201, done);
|
||||
});
|
||||
|
||||
describe('config.customUserService = true', function () {
|
||||
it('should add new maintainer fail when user not exists', function (done) {
|
||||
mm(config, 'customUserService', true);
|
||||
request(app)
|
||||
.put('/@cnpm/test-package-maintainer/-rev/1')
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send({
|
||||
maintainers: [
|
||||
{ name: 'new-maintainer-not-exists', email: 'new-maintainer@cnpmjs.org' },
|
||||
{ name: 'new-maintainer-not-exists2', email: 'new-maintainer@cnpmjs.org' },
|
||||
{ name: utils.admin, email: utils.admin + '@cnpmjs.org' },
|
||||
]
|
||||
})
|
||||
.expect({
|
||||
error: 'invalid user name',
|
||||
reason: 'User: new-maintainer-not-exists, new-maintainer-not-exists2 not exists'
|
||||
})
|
||||
.expect(403, done);
|
||||
});
|
||||
|
||||
it('should add new maintainer success when user all exists', function (done) {
|
||||
mm(config, 'customUserService', true);
|
||||
request(app)
|
||||
.put('/@cnpm/test-package-maintainer/-rev/1')
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send({
|
||||
maintainers: [
|
||||
{ name: 'cnpmjstest101', email: 'cnpmjstest101@cnpmjs.org' },
|
||||
{ name: 'cnpmjstest102', email: 'cnpmjstest102@cnpmjs.org' },
|
||||
{ name: utils.admin, email: utils.admin + '@cnpmjs.org' },
|
||||
]
|
||||
})
|
||||
.expect({
|
||||
ok: true,
|
||||
id: '@cnpm/test-package-maintainer',
|
||||
rev: '1'
|
||||
})
|
||||
.expect(201, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
804
test/controllers/registry/module/public_mode.test.js
Normal file
804
test/controllers/registry/module/public_mode.test.js
Normal file
@@ -0,0 +1,804 @@
|
||||
/*!
|
||||
* cnpmjs.org - test/controllers/registry/module/public_mode.test.js
|
||||
* Copyright(c) 2014 dead_horse <dead_horse@qq.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var should = require('should');
|
||||
var request = require('supertest');
|
||||
var path = require('path');
|
||||
var mm = require('mm');
|
||||
var pedding = require('pedding');
|
||||
var config = require('../../../../config');
|
||||
var app = require('../../../../servers/registry');
|
||||
var utils = require('../../../utils');
|
||||
var Module = require('../../../../proxy/module');
|
||||
|
||||
var fixtures = path.join(__dirname, '..', '..', '..', 'fixtures');
|
||||
|
||||
describe('controllers/registry/module/public_module.test.js', function () {
|
||||
beforeEach(function () {
|
||||
mm(config, 'enablePrivate', false);
|
||||
});
|
||||
before(function (done) {
|
||||
mm(config, 'enablePrivate', false);
|
||||
mm(config, 'forcePublishWithScope', false);
|
||||
app = app.listen(0, function () {
|
||||
done = pedding(2, done);
|
||||
// name: publictestmodule
|
||||
var pkg = utils.getPackage('publictestmodule', '0.0.1', utils.otherUser);
|
||||
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(201, function (err) {
|
||||
should.not.exist(err);
|
||||
pkg = utils.getPackage('publictestmodule', '0.0.2', utils.otherUser);
|
||||
// publish 0.0.2
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(201, done);
|
||||
});
|
||||
|
||||
// publicputmodule@0.1.9
|
||||
var testpkg = utils.getPackage('publicputmodule', '0.1.9', utils.otherUser);
|
||||
|
||||
request(app)
|
||||
.put('/' + testpkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(201, done);
|
||||
});
|
||||
});
|
||||
afterEach(mm.restore);
|
||||
|
||||
describe('PUT /:name/-rev/id updateMaintainers() in public mode', function () {
|
||||
beforeEach(function () {
|
||||
mm(config, 'forcePublishWithScope', false);
|
||||
});
|
||||
|
||||
before(function (done) {
|
||||
mm(config, 'enablePrivate', false);
|
||||
mm(config, 'forcePublishWithScope', false);
|
||||
request(app)
|
||||
.put('/publictestmodule/-rev/1')
|
||||
.send({
|
||||
maintainers: [{
|
||||
name: 'cnpmjstest101',
|
||||
email: 'fengmk2@cnpmjs.org'
|
||||
}]
|
||||
})
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect({"ok":true,"id":"publictestmodule","rev":"1"}, done);
|
||||
});
|
||||
|
||||
it('should add new maintainers', function (done) {
|
||||
request(app)
|
||||
.put('/publictestmodule/-rev/1')
|
||||
.send({
|
||||
maintainers: [{
|
||||
name: 'cnpmjstest10',
|
||||
email: 'fengmk2@cnpmjs.org'
|
||||
}, {
|
||||
name: 'cnpmjstest101',
|
||||
email: 'fengmk2@cnpmjs.org'
|
||||
}]
|
||||
})
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect(201)
|
||||
.expect({
|
||||
ok: true, id: 'publictestmodule', rev: '1'
|
||||
}, function (err) {
|
||||
should.not.exist(err);
|
||||
done = pedding(2, done);
|
||||
// check maintainers update
|
||||
request(app)
|
||||
.get('/publictestmodule')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var pkg = res.body;
|
||||
pkg.maintainers.should.length(2);
|
||||
pkg.maintainers.should.eql(pkg.versions['0.0.1'].maintainers);
|
||||
pkg.maintainers.sort(function (a, b) {
|
||||
return a.name > b.name ? 1 : -1;
|
||||
});
|
||||
pkg.maintainers.should.eql([
|
||||
{ name: 'cnpmjstest10', email: 'fengmk2@gmail.com' },
|
||||
{ name: 'cnpmjstest101', email: 'fengmk2@gmail.com' },
|
||||
]);
|
||||
done();
|
||||
});
|
||||
|
||||
// /pkg/0.0.1
|
||||
request(app)
|
||||
.get('/publictestmodule/0.0.1')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var pkg = res.body;
|
||||
pkg.maintainers.should.length(2);
|
||||
pkg.maintainers.sort(function (a, b) {
|
||||
return a.name > b.name ? 1 : -1;
|
||||
});
|
||||
pkg.maintainers.should.eql([
|
||||
{ name: 'cnpmjstest10', email: 'fengmk2@gmail.com' },
|
||||
{ name: 'cnpmjstest101', email: 'fengmk2@gmail.com' },
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should add again new maintainers', function (done) {
|
||||
request(app)
|
||||
.put('/publictestmodule/-rev/1')
|
||||
.send({
|
||||
maintainers: [{
|
||||
name: 'cnpmjstest101',
|
||||
email: 'cnpmjstest101@cnpmjs.org'
|
||||
}, {
|
||||
name: 'fengmk2',
|
||||
email: 'fengmk2@cnpmjs.org'
|
||||
}]
|
||||
})
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect(201)
|
||||
.expect('content-type', 'application/json; charset=utf-8', done);
|
||||
});
|
||||
|
||||
it('should add new maintainers by admin', function (done) {
|
||||
request(app)
|
||||
.put('/publictestmodule/-rev/1')
|
||||
.send({
|
||||
maintainers: [{
|
||||
name: 'cnpmjstest101',
|
||||
email: 'cnpmjstest101@cnpmjs.org'
|
||||
}, {
|
||||
name: 'fengmk2',
|
||||
email: 'fengmk2@cnpmjs.org'
|
||||
}]
|
||||
})
|
||||
.set('authorization', utils.adminAuth)
|
||||
.expect(201)
|
||||
.expect('content-type', 'application/json; charset=utf-8', done);
|
||||
});
|
||||
|
||||
it('should rm maintainers', function (done) {
|
||||
request(app)
|
||||
.put('/publictestmodule/-rev/1')
|
||||
.send({
|
||||
maintainers: [{
|
||||
name: 'cnpmjstest101',
|
||||
email: 'cnpmjstest101@cnpmjs.org'
|
||||
}]
|
||||
})
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect(201)
|
||||
.expect('content-type', 'application/json; charset=utf-8', done);
|
||||
});
|
||||
|
||||
it('should rm again maintainers', function (done) {
|
||||
request(app)
|
||||
.put('/publictestmodule/-rev/1')
|
||||
.send({
|
||||
maintainers: [{
|
||||
name: 'cnpmjstest101',
|
||||
email: 'cnpmjstest101@cnpmjs.org'
|
||||
}]
|
||||
})
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect(201)
|
||||
.expect({
|
||||
id: 'publictestmodule',
|
||||
rev: '1',
|
||||
ok: true
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should rm all maintainers forbidden 403', function (done) {
|
||||
request(app)
|
||||
.put('/publictestmodule/-rev/1')
|
||||
.send({
|
||||
maintainers: []
|
||||
})
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect(403)
|
||||
.expect({error: 'invalid operation', reason: 'Can not remove all maintainers'})
|
||||
.expect('content-type', 'application/json; charset=utf-8', done);
|
||||
});
|
||||
|
||||
it('should 403 when not maintainer update', function (done) {
|
||||
request(app)
|
||||
.put('/publictestmodule/-rev/1')
|
||||
.send({
|
||||
maintainers: [{
|
||||
name: 'cnpmjstest10',
|
||||
email: 'cnpmjstest10@cnpmjs.org'
|
||||
}]
|
||||
})
|
||||
.set('authorization', utils.secondUserAuth)
|
||||
.expect(403)
|
||||
.expect({
|
||||
error: 'forbidden user',
|
||||
reason: 'cnpmjstest102 not authorized to modify publictestmodule'
|
||||
}, done);
|
||||
});
|
||||
|
||||
describe('forcePublishWithScope = true', function () {
|
||||
beforeEach(function () {
|
||||
mm(config, 'forcePublishWithScope', true);
|
||||
});
|
||||
|
||||
before(function (done) {
|
||||
mm(config, 'forcePublishWithScope', true);
|
||||
mm(config, 'enablePrivate', false);
|
||||
var pkg = utils.getPackage('@cnpm/publictestmodule', '0.0.1', utils.otherUser);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(201, function (err, res) {
|
||||
should.not.exist(err);
|
||||
pkg = utils.getPackage(pkg.name, '0.0.2', utils.otherUser);
|
||||
// publish 0.0.2
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(201, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should 403 add maintainers without scope', function (done) {
|
||||
request(app)
|
||||
.put('/publictestmodule/-rev/1')
|
||||
.send({
|
||||
maintainers: [{
|
||||
name: 'cnpmjstest101',
|
||||
email: 'cnpmjstest101@cnpmjs.org'
|
||||
}, {
|
||||
name: 'fengmk2',
|
||||
email: 'fengmk2@cnpmjs.org'
|
||||
}]
|
||||
})
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect(403, done);
|
||||
});
|
||||
|
||||
it('should add maintainers ok with scope', function (done) {
|
||||
request(app)
|
||||
.put('/@cnpm/publictestmodule/-rev/1')
|
||||
.send({
|
||||
maintainers: [{
|
||||
name: 'cnpmjstest101',
|
||||
email: 'cnpmjstest101@cnpmjs.org'
|
||||
}, {
|
||||
name: 'fengmk2',
|
||||
email: 'fengmk2@cnpmjs.org'
|
||||
}]
|
||||
})
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect( { ok: true, id: '@cnpm/publictestmodule', rev: '1' })
|
||||
.expect(201, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /:name publish new flow addPackageAndDist()', function () {
|
||||
beforeEach(function () {
|
||||
mm(config, 'enablePrivate', false);
|
||||
mm(config, 'forcePublishWithScope', false);
|
||||
});
|
||||
|
||||
it('should publish with tgz base64, addPackageAndDist()', function (done) {
|
||||
var pkg = utils.getPackage('publicpublishmodule', '0.0.2', utils.otherUser);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(201, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'rev');
|
||||
res.body.ok.should.equal(true);
|
||||
pkg = utils.getPackage('publicpublishmodule', '0.0.2', utils.otherUser);
|
||||
// upload again should 403
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(403, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.eql({
|
||||
error: 'forbidden',
|
||||
reason: 'cannot modify pre-existing version: 0.0.2'
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should other user pulbish 403', function (done) {
|
||||
var pkg = utils.getPackage('publicpublishmodule', '0.0.3', utils.secondUser);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.secondUserAuth)
|
||||
.send(pkg)
|
||||
.expect(403, done);
|
||||
});
|
||||
|
||||
it('should admin pulbish 403', function (done) {
|
||||
var pkg = utils.getPackage('publicpublishmodule', '0.0.3', utils.admin);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send(pkg)
|
||||
.expect(403, done);
|
||||
});
|
||||
|
||||
it('should publish with scope, addPackageAndDist()', function (done) {
|
||||
mm(config, 'forcePublishWithScope', false);
|
||||
var pkg = utils.getPackage('@cnpm/publicpublishmodule', '0.0.2', utils.otherUser);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(201, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'rev');
|
||||
res.body.ok.should.equal(true);
|
||||
|
||||
// upload again should 403
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(403, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.eql({
|
||||
error: 'forbidden',
|
||||
reason: 'cannot modify pre-existing version: 0.0.2'
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('forcePublishWithScope = true', function () {
|
||||
it('should publish without scope 403, addPackageAndDist()', function (done) {
|
||||
mm(config, 'forcePublishWithScope', false);
|
||||
var pkg = utils.getPackage('publicpublishmodule', '0.0.2');
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(403, done);
|
||||
});
|
||||
|
||||
it('should admin publish without scope ok, addPackageAndDist()', function (done) {
|
||||
mm(config, 'forcePublishWithScope', false);
|
||||
var pkg = utils.getPackage('publicpublishmodule1', '0.0.4', utils.admin);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send(pkg)
|
||||
.expect(201, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /:name/-rev/:rev removeWithVersions', function () {
|
||||
var withoutScopeRev;
|
||||
before(function (done) {
|
||||
mm(config, 'enablePrivate', false);
|
||||
mm(config, 'forcePublishWithScope', false);
|
||||
var pkg = utils.getPackage('publicremovemodule', '0.0.1', utils.otherUser);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(201, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'rev');
|
||||
res.body.ok.should.equal(true);
|
||||
|
||||
pkg = utils.getPackage('publicremovemodule', '0.0.2', utils.otherUser);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(201, function (err, res) {
|
||||
should.not.exist(err);
|
||||
withoutScopeRev = res.body.rev;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove with version ok', function (done) {
|
||||
mm.empty(Module, 'removeByNameAndVersions');
|
||||
mm.empty(Module, 'removeTagsByIds');
|
||||
request(app)
|
||||
.put('/publicremovemodule/-rev/' + withoutScopeRev)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send({
|
||||
versions: {
|
||||
'0.0.1': {}
|
||||
}
|
||||
})
|
||||
.expect(201, done);
|
||||
});
|
||||
|
||||
it('should no auth user remove 403', function (done) {
|
||||
mm.empty(Module, 'removeByNameAndVersions');
|
||||
mm.empty(Module, 'removeTagsByIds');
|
||||
request(app)
|
||||
.put('/publicremovemodule/-rev/' + withoutScopeRev)
|
||||
.set('authorization', utils.secondUserAuth)
|
||||
.send({
|
||||
versions: {
|
||||
'0.0.1': {}
|
||||
}
|
||||
})
|
||||
.expect(403, done);
|
||||
});
|
||||
|
||||
it('should admin remove ok', function (done) {
|
||||
mm.empty(Module, 'removeByNameAndVersions');
|
||||
mm.empty(Module, 'removeTagsByIds');
|
||||
request(app)
|
||||
.put('/publicremovemodule/-rev/' + withoutScopeRev)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send({
|
||||
versions: {
|
||||
'0.0.1': {}
|
||||
}
|
||||
})
|
||||
.expect(201, done);
|
||||
});
|
||||
|
||||
describe('forcePublishWithScope = true', function () {
|
||||
var withScopeRev;
|
||||
before(function (done) {
|
||||
mm(config, 'enablePrivate', false);
|
||||
mm(config, 'forcePublishWithScope', true);
|
||||
var pkg = utils.getPackage('@cnpm/publicremovemodule', '0.0.1', utils.otherUser);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(201, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'rev');
|
||||
res.body.ok.should.equal(true);
|
||||
|
||||
pkg = utils.getPackage('@cnpm/publicremovemodule', '0.0.2', utils.otherUser);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(201, function (err, res) {
|
||||
should.not.exist(err);
|
||||
withScopeRev = res.body.rev;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove without scope 403', function (done) {
|
||||
mm(config, 'forcePublishWithScope', true);
|
||||
mm.empty(Module, 'removeByNameAndVersions');
|
||||
mm.empty(Module, 'removeTagsByIds');
|
||||
request(app)
|
||||
.put('/publicremovemodule/-rev/' + withoutScopeRev)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send({
|
||||
versions: {
|
||||
'0.0.1': {}
|
||||
}
|
||||
})
|
||||
.expect(403, done);
|
||||
});
|
||||
|
||||
it('should admin remove without scope ok', function (done) {
|
||||
mm(config, 'forcePublishWithScope', true);
|
||||
mm.empty(Module, 'removeByNameAndVersions');
|
||||
mm.empty(Module, 'removeTagsByIds');
|
||||
request(app)
|
||||
.put('/publicremovemodule/-rev/' + withoutScopeRev)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send({
|
||||
versions: {
|
||||
'0.0.1': {}
|
||||
}
|
||||
})
|
||||
.expect(201, done);
|
||||
});
|
||||
|
||||
it('should remove with scope ok', function (done) {
|
||||
mm(config, 'forcePublishWithScope', true);
|
||||
mm.empty(Module, 'removeByNameAndVersions');
|
||||
mm.empty(Module, 'removeTagsByIds');
|
||||
request(app)
|
||||
.put('/@cnpm/publicremovemodule/-rev/' + withScopeRev)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send({
|
||||
versions: {
|
||||
'0.0.1': {}
|
||||
}
|
||||
})
|
||||
.expect(201, done);
|
||||
});
|
||||
|
||||
it('should admin remove with scope ok', function (done) {
|
||||
mm(config, 'forcePublishWithScope', true);
|
||||
mm.empty(Module, 'removeByNameAndVersions');
|
||||
mm.empty(Module, 'removeTagsByIds');
|
||||
request(app)
|
||||
.put('/@cnpm/publicremovemodule/-rev/' + withScopeRev)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send({
|
||||
versions: {
|
||||
'0.0.1': {}
|
||||
}
|
||||
})
|
||||
.expect(201, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE /:name/download/:filename/-rev/:rev', function () {
|
||||
var withoutScopeRev;
|
||||
beforeEach(function () {
|
||||
mm(config, 'enablePrivate', false);
|
||||
mm(config, 'forcePublishWithScope', false);
|
||||
});
|
||||
beforeEach(function (done) {
|
||||
mm(config, 'enablePrivate', false);
|
||||
mm(config, 'forcePublishWithScope', false);
|
||||
var pkg = utils.getPackage('public-test-delete-download-module', '0.1.9', utils.otherUser);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('content-type', 'application/json')
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.end(function (err, res) {
|
||||
should.not.exist(err);
|
||||
if (res.body.rev) {
|
||||
withoutScopeRev = res.body.rev;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete 403 when auth error', function (done) {
|
||||
request(app)
|
||||
.del('/public-test-delete-download-module/download/public-test-delete-download-module-0.1.9.tgz/-rev/' + withoutScopeRev)
|
||||
.set('authorization', utils.secondUserAuth)
|
||||
.expect(403, done);
|
||||
});
|
||||
|
||||
it('should delete file ok', function (done) {
|
||||
request(app)
|
||||
.del('/public-test-delete-download-module/download/public-test-delete-download-module-0.1.9.tgz/-rev/' + withoutScopeRev)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it('should admin delete file ok', function (done) {
|
||||
request(app)
|
||||
.del('/public-test-delete-download-module/download/public-test-delete-download-module-0.1.9.tgz/-rev/' + withoutScopeRev)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
describe('forcePublishWithScope = true', function () {
|
||||
var withScopeRev;
|
||||
beforeEach(function () {
|
||||
mm(config, 'enablePrivate', false);
|
||||
mm(config, 'forcePublishWithScope', true);
|
||||
});
|
||||
beforeEach(function (done) {
|
||||
mm(config, 'enablePrivate', false);
|
||||
mm(config, 'forcePublishWithScope', true);
|
||||
var pkg = utils.getPackage('@cnpm/public-test-delete-download-module', '0.1.9', utils.otherUser);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('content-type', 'application/json')
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.end(function (err, res) {
|
||||
should.not.exist(err);
|
||||
if (res.body.rev) {
|
||||
withScopeRev = res.body.rev;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete file without scope 403', function (done) {
|
||||
request(app)
|
||||
.del('/public-test-delete-download-module/download/public-test-delete-download-module-0.1.9.tgz/-rev/' + withoutScopeRev)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect(403, done);
|
||||
});
|
||||
|
||||
it('should admin delete file without scope ok', function (done) {
|
||||
request(app)
|
||||
.del('/public-test-delete-download-module/download/public-test-delete-download-module-0.1.9.tgz/-rev/' + withoutScopeRev)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.expect(200, done);
|
||||
});
|
||||
it('should delete file with scope ok', function (done) {
|
||||
request(app)
|
||||
.del('/@cnpm/public-test-delete-download-module/download/@cnpm/public-test-delete-download-module-0.1.9.tgz/-rev/' + withScopeRev)
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it('should admin delete file with scope ok', function (done) {
|
||||
request(app)
|
||||
.del('/@cnpm/public-test-delete-download-module/download/@cnpm/public-test-delete-download-module-0.1.9.tgz/-rev/' + withScopeRev)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.expect(200, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /:name/:tag updateTag()', function () {
|
||||
it('should create new tag ok', function (done) {
|
||||
request(app)
|
||||
.put('/publictestmodule/newtag')
|
||||
.set('content-type', 'application/json')
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send('"0.0.1"')
|
||||
.expect(201)
|
||||
.expect({"ok":true}, done);
|
||||
});
|
||||
|
||||
it('shold update tag not maintainer 403', function (done) {
|
||||
request(app)
|
||||
.put('/publictestmodule/newtag')
|
||||
.set('content-type', 'application/json')
|
||||
.set('authorization', utils.secondUserAuth)
|
||||
.send('"0.0.1"')
|
||||
.expect(403, done);
|
||||
});
|
||||
|
||||
it('should admin update tag ok', function (done) {
|
||||
request(app)
|
||||
.put('/publictestmodule/newtag')
|
||||
.set('content-type', 'application/json')
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send('"0.0.1"')
|
||||
.expect(201, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE /:name/-rev/:rev', function () {
|
||||
describe('remove all modules by name', function () {
|
||||
beforeEach(function () {
|
||||
mm(config, 'enablePrivate', false);
|
||||
mm(config, 'forcePublishWithScope', false);
|
||||
});
|
||||
before(function (done) {
|
||||
mm(config, 'enablePrivate', false);
|
||||
mm(config, 'forcePublishWithScope', false);
|
||||
var pkg = utils.getPackage('public-remove-all-module', '0.0.1', utils.otherUser);
|
||||
request(app)
|
||||
.put('/public-remove-all-module')
|
||||
.set('content-type', 'application/json')
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(201, done);
|
||||
});
|
||||
|
||||
it('shold fail when user not maintainer', function (done) {
|
||||
request(app)
|
||||
.del('/public-remove-all-module/-rev/1')
|
||||
.set('authorization', utils.secondUserAuth)
|
||||
.expect(403, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.eql({
|
||||
error: 'forbidden user',
|
||||
reason: 'cnpmjstest102 not authorized to modify public-remove-all-module'
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('shold maintainer remove ok', function (done) {
|
||||
mm.empty(Module, 'removeByName');
|
||||
mm.empty(Module, 'removeTags');
|
||||
request(app)
|
||||
.del('/public-remove-all-module/-rev/1')
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(res.headers['set-cookie']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('shold admin remove ok', function (done) {
|
||||
mm.empty(Module, 'removeByName');
|
||||
mm.empty(Module, 'removeTags');
|
||||
request(app)
|
||||
.del('/public-remove-all-module/-rev/1')
|
||||
.set('authorization', utils.adminAuth)
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(res.headers['set-cookie']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('forcePublishWithScope = true', function () {
|
||||
before(function (done) {
|
||||
mm(config, 'enablePrivate', false);
|
||||
mm(config, 'forcePublishWithScope', true);
|
||||
var pkg = utils.getPackage('@cnpm/public-remove-all-module', '0.0.1', utils.otherUser);
|
||||
request(app)
|
||||
.put('/@cnpm/public-remove-all-module')
|
||||
.set('content-type', 'application/json')
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.send(pkg)
|
||||
.expect(201, done);
|
||||
});
|
||||
|
||||
it('should fail when user remove module without scope', function (done) {
|
||||
mm(config, 'forcePublishWithScope', true);
|
||||
request(app)
|
||||
.del('/public-remove-all-module/-rev/1')
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect(403, done);
|
||||
});
|
||||
|
||||
it('shold admin remove module without scope ok', function (done) {
|
||||
mm(config, 'forcePublishWithScope', true);
|
||||
mm.empty(Module, 'removeByName');
|
||||
mm.empty(Module, 'removeTags');
|
||||
request(app)
|
||||
.del('/public-remove-all-module/-rev/1')
|
||||
.set('authorization', utils.adminAuth)
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it('shold maintainer remove ok', function (done) {
|
||||
mm(config, 'forcePublishWithScope', true);
|
||||
mm.empty(Module, 'removeByName');
|
||||
mm.empty(Module, 'removeTags');
|
||||
request(app)
|
||||
.del('/@cnpm/public-remove-all-module/-rev/1')
|
||||
.set('authorization', utils.otherUserAuth)
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(res.headers['set-cookie']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('shold admin remove ok', function (done) {
|
||||
mm(config, 'forcePublishWithScope', true);
|
||||
mm.empty(Module, 'removeByName');
|
||||
mm.empty(Module, 'removeTags');
|
||||
request(app)
|
||||
.del('/@cnpm/public-remove-all-module/-rev/1')
|
||||
.set('authorization', utils.adminAuth)
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(res.headers['set-cookie']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
279
test/controllers/registry/module/scope_package.test.js
Normal file
279
test/controllers/registry/module/scope_package.test.js
Normal file
@@ -0,0 +1,279 @@
|
||||
/**!
|
||||
* cnpmjs.org - test/controllers/registry/module/scope_package.test.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var should = require('should');
|
||||
var request = require('supertest');
|
||||
var mm = require('mm');
|
||||
var config = require('../../../../config');
|
||||
var app = require('../../../../servers/registry');
|
||||
var utils = require('../../../utils');
|
||||
var Module = require('../../../../proxy/module');
|
||||
|
||||
describe('controllers/registry/module/scope_package.test.js', function () {
|
||||
var pkgname = '@cnpm/test-scope-package';
|
||||
var pkgURL = '/@' + encodeURIComponent(pkgname.substring(1));
|
||||
before(function (done) {
|
||||
app = app.listen(0, function () {
|
||||
// add scope package
|
||||
var pkg = utils.getPackage(pkgname, '0.0.1', utils.admin);
|
||||
|
||||
request(app)
|
||||
.put(pkgURL)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send(pkg)
|
||||
.expect(201, function (err) {
|
||||
should.not.exist(err);
|
||||
pkg = utils.getPackage(pkgname, '0.0.2', utils.admin);
|
||||
// publish 0.0.2
|
||||
request(app.listen())
|
||||
.put(pkgURL)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send(pkg)
|
||||
.expect(201, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
mm(config, 'scopes', ['@cnpm', '@cnpmtest']);
|
||||
});
|
||||
|
||||
afterEach(mm.restore);
|
||||
|
||||
it('should get 404 when do not support scope', function (done) {
|
||||
mm(config, 'scopes', []);
|
||||
request(app)
|
||||
.get('/@invalid/test')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should get 400 when scope not match', function (done) {
|
||||
request(app)
|
||||
.get('/@invalid/test')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should get scope package info: /@scope%2Fname', function (done) {
|
||||
request(app)
|
||||
.get(pkgURL)
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var pkg = res.body;
|
||||
pkg.name.should.equal(pkgname);
|
||||
pkg.versions.should.have.keys('0.0.1', '0.0.2');
|
||||
pkg['dist-tags'].latest.should.equal('0.0.2');
|
||||
pkg.versions['0.0.1'].name.should.equal(pkgname);
|
||||
pkg.versions['0.0.1'].dist.tarball
|
||||
.should.containEql('/@cnpm/test-scope-package/download/@cnpm/test-scope-package-0.0.1.tgz');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should get scope package info: /@scope/name', function (done) {
|
||||
request(app.listen())
|
||||
.get('/' + pkgname)
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var pkg = res.body;
|
||||
pkg.name.should.equal(pkgname);
|
||||
pkg.versions.should.have.keys('0.0.1', '0.0.2');
|
||||
pkg['dist-tags'].latest.should.equal('0.0.2');
|
||||
pkg.versions['0.0.1'].name.should.equal(pkgname);
|
||||
pkg.versions['0.0.1'].dist.tarball
|
||||
.should.containEql('/@cnpm/test-scope-package/download/@cnpm/test-scope-package-0.0.1.tgz');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should get scope package info: /%40scope%2Fname', function (done) {
|
||||
request(app)
|
||||
.get('/' + encodeURIComponent(pkgname))
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var pkg = res.body;
|
||||
pkg.name.should.equal(pkgname);
|
||||
pkg.versions.should.have.keys('0.0.1', '0.0.2');
|
||||
pkg['dist-tags'].latest.should.equal('0.0.2');
|
||||
pkg.versions['0.0.1'].name.should.equal(pkgname);
|
||||
pkg.versions['0.0.1'].dist.tarball
|
||||
.should.containEql('/@cnpm/test-scope-package/download/@cnpm/test-scope-package-0.0.1.tgz');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should get scope package with version', function (done) {
|
||||
request(app)
|
||||
.get('/' + pkgname + '/0.0.1')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var pkg = res.body;
|
||||
pkg.name.should.equal(pkgname);
|
||||
pkg.version.should.equal('0.0.1');
|
||||
pkg.dist.tarball
|
||||
.should.containEql('/@cnpm/test-scope-package/download/@cnpm/test-scope-package-0.0.1.tgz');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should get scope package with tag', function (done) {
|
||||
request(app)
|
||||
.get('/' + pkgname + '/latest')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var pkg = res.body;
|
||||
pkg.name.should.equal(pkgname);
|
||||
pkg.version.should.equal('0.0.2');
|
||||
pkg.dist.tarball
|
||||
.should.containEql('/@cnpm/test-scope-package/download/@cnpm/test-scope-package-0.0.2.tgz');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should download work', function (done) {
|
||||
request(app)
|
||||
.get('/@cnpm/test-scope-package/download/@cnpm/test-scope-package-0.0.2.tgz')
|
||||
.expect('Location', /\.tgz$/)
|
||||
.expect(302, done);
|
||||
});
|
||||
|
||||
describe('support adaptScope', function () {
|
||||
before(function (done) {
|
||||
var pkg = utils.getPackage('test-default-scope-package', '0.0.1', utils.admin);
|
||||
request(app)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send(pkg)
|
||||
.expect(201, done);
|
||||
});
|
||||
describe('/@:scope/:name', function () {
|
||||
it('should adapt /@cnpm/test-default-scope-package => /test-default-scope-package', function (done) {
|
||||
mm(config, 'adaptScope', true);
|
||||
request(app)
|
||||
.get('/@cnpm/test-default-scope-package')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var pkg = res.body;
|
||||
pkg._id.should.equal('@cnpm/test-default-scope-package');
|
||||
pkg.name.should.equal('@cnpm/test-default-scope-package');
|
||||
pkg.versions.should.have.keys('0.0.1');
|
||||
pkg['dist-tags'].latest.should.equal('0.0.1');
|
||||
pkg.versions['0.0.1'].name.should.equal('@cnpm/test-default-scope-package');
|
||||
pkg.versions['0.0.1']._id.should.equal('@cnpm/test-default-scope-package@0.0.1');
|
||||
pkg.versions['0.0.1'].dist.tarball
|
||||
.should.containEql('/test-default-scope-package/download/test-default-scope-package-0.0.1.tgz');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should adapt /@cnpmtest/test-default-scope-package => /test-default-scope-package', function (done) {
|
||||
mm(config, 'adaptScope', true);
|
||||
request(app)
|
||||
.get('/@cnpmtest/test-default-scope-package')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var pkg = res.body;
|
||||
pkg._id.should.equal('@cnpmtest/test-default-scope-package');
|
||||
pkg.name.should.equal('@cnpmtest/test-default-scope-package');
|
||||
pkg.versions.should.have.keys('0.0.1');
|
||||
pkg['dist-tags'].latest.should.equal('0.0.1');
|
||||
pkg.versions['0.0.1'].name.should.equal('@cnpmtest/test-default-scope-package');
|
||||
pkg.versions['0.0.1']._id.should.equal('@cnpmtest/test-default-scope-package@0.0.1');
|
||||
pkg.versions['0.0.1'].dist.tarball
|
||||
.should.containEql('/test-default-scope-package/download/test-default-scope-package-0.0.1.tgz');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not adapt when adaptScope is false', function (done) {
|
||||
mm(config, 'adaptScope', false);
|
||||
request(app)
|
||||
.get('/@cnpm/test-default-scope-package')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should 404 when pkg not exists', function (done) {
|
||||
mm(config, 'adaptScope', true);
|
||||
request(app)
|
||||
.get('/@cnpm/test-default-scope-package-not-exists')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should show() 404 when adapt package is not private package', function (done) {
|
||||
var getByTag = Module.getByTag;
|
||||
mm(Module, 'getByTag', function* (name, tag) {
|
||||
var pkg = yield getByTag.call(Module, name, tag);
|
||||
pkg && delete pkg.package._publish_on_cnpm;
|
||||
return pkg;
|
||||
});
|
||||
mm(config, 'adaptScope', true);
|
||||
request(app)
|
||||
.get('/@cnpm/test-default-scope-package')
|
||||
.expect(404, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('/@:scope/:name/:tag', function () {
|
||||
it('should adapt /@cnpm/test-default-scope-package/latest => /test-default-scope-package/latest', function (done) {
|
||||
mm(config, 'adaptScope', true);
|
||||
request(app)
|
||||
.get('/@cnpm/test-default-scope-package/latest')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var pkg = res.body;
|
||||
pkg.version.should.have.equal('0.0.1');
|
||||
pkg.name.should.equal('@cnpm/test-default-scope-package');
|
||||
pkg._id.should.equal('@cnpm/test-default-scope-package@0.0.1');
|
||||
pkg.dist.tarball.should.containEql('/test-default-scope-package/download/test-default-scope-package-0.0.1.tgz');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not adapt when adaptScope is false', function (done) {
|
||||
mm(config, 'adaptScope', false);
|
||||
request(app)
|
||||
.get('/@cnpm/test-default-scope-package/latest')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should 404 when pkg not exists', function (done) {
|
||||
mm(config, 'adaptScope', true);
|
||||
request(app)
|
||||
.get('/@cnpm/test-default-scope-package-not-exists/latest')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should 404 when pkg version not exists', function (done) {
|
||||
mm(config, 'adaptScope', true);
|
||||
request(app)
|
||||
.get('/@cnpm/test-default-scope-package-not-exists/1.0.0')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should get() 404 when adapt package is not private package', function (done) {
|
||||
var getByTag = Module.getByTag;
|
||||
mm(Module, 'getByTag', function* (name, tag) {
|
||||
var pkg = yield getByTag.call(Module, name, tag);
|
||||
pkg && delete pkg.package._publish_on_cnpm;
|
||||
return pkg;
|
||||
});
|
||||
mm(config, 'adaptScope', true);
|
||||
request(app)
|
||||
.get('/@cnpm/test-default-scope-package/latest')
|
||||
.expect(404, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - test/controllers/registry/user.test.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -20,14 +20,12 @@ var mm = require('mm');
|
||||
var app = require('../../../servers/registry');
|
||||
var user = require('../../../proxy/user');
|
||||
var mysql = require('../../../common/mysql');
|
||||
var config = require('../../../config');
|
||||
var UserService = require('../../../services/user');
|
||||
|
||||
describe('controllers/registry/user.test.js', function () {
|
||||
before(function (done) {
|
||||
app.listen(0, done);
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
app.close(done);
|
||||
app = app.listen(0, done);
|
||||
});
|
||||
|
||||
afterEach(mm.restore);
|
||||
@@ -35,11 +33,26 @@ describe('controllers/registry/user.test.js', function () {
|
||||
describe('GET /-/user/org.couchdb.user:name', function () {
|
||||
it('should return user info', function (done) {
|
||||
request(app)
|
||||
.get('/-/user/org.couchdb.user:cnpmjstest1')
|
||||
.get('/-/user/org.couchdb.user:cnpmjstest10')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('_id', '_rev', 'name', 'email', 'type', 'roles', 'date');
|
||||
res.body.name.should.equal('cnpmjstest1');
|
||||
res.body.should.have.keys('_id', '_rev', 'name', 'email', 'type',
|
||||
'_cnpm_meta', 'roles', 'date');
|
||||
res.body.name.should.equal('cnpmjstest10');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should return npm user info', function (done) {
|
||||
request(app)
|
||||
.get('/-/user/org.couchdb.user:fengmk2')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.name.should.equal('fengmk2');
|
||||
res.body.github.should.equal('fengmk2');
|
||||
res.body._cnpm_meta.should.have.keys('id', 'npm_user', 'gmt_create',
|
||||
'gmt_modified', 'admin');
|
||||
res.body._cnpm_meta.admin.should.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -73,86 +86,51 @@ describe('controllers/registry/user.test.js', function () {
|
||||
});
|
||||
|
||||
it('should 409 when already exist', function (done) {
|
||||
mm.data(user, 'get', {name: 'name'});
|
||||
mm(user, 'get', function* () {
|
||||
return {name: 'name'};
|
||||
});
|
||||
request(app)
|
||||
.put('/-/user/org.couchdb.user:name')
|
||||
.send({
|
||||
name: 'name',
|
||||
salt: 'salt',
|
||||
password_sha: 'password_sha',
|
||||
password: 'password',
|
||||
email: 'email'
|
||||
})
|
||||
.expect(409, done);
|
||||
});
|
||||
|
||||
it('should 500 when user.get error', function (done) {
|
||||
mm.error(user, 'get', 'mock error');
|
||||
mm(user, 'get', function* () {
|
||||
throw new Error('mock User.get error');
|
||||
});
|
||||
request(app)
|
||||
.put('/-/user/org.couchdb.user:name')
|
||||
.send({
|
||||
name: 'name',
|
||||
salt: 'salt',
|
||||
password_sha: 'password_sha',
|
||||
password: 'password',
|
||||
email: 'email'
|
||||
})
|
||||
.expect(500, done);
|
||||
});
|
||||
|
||||
it('should 201 when user.add ok', function (done) {
|
||||
mm.empty(user, 'get');
|
||||
mm.data(user, 'add', {rev: '1-123'});
|
||||
mm(user, 'get', function* () {
|
||||
return null;
|
||||
});
|
||||
mm(user, 'add', function* () {
|
||||
return {rev: '1-123'};
|
||||
});
|
||||
request(app)
|
||||
.put('/-/user/org.couchdb.user:name')
|
||||
.send({
|
||||
name: 'name',
|
||||
salt: 'salt',
|
||||
password_sha: 'password_sha',
|
||||
password: 'password',
|
||||
email: 'email'
|
||||
})
|
||||
.expect(201, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /_session', function () {
|
||||
it('should 500 auth error by user.auth', function (done) {
|
||||
mm.error(user, 'auth', 'mock error');
|
||||
request(app)
|
||||
.post('/_session')
|
||||
.send({
|
||||
name: 'name',
|
||||
password: '123'
|
||||
})
|
||||
.expect(500, done);
|
||||
});
|
||||
|
||||
it('should 401 auth fail by user.auth', function (done) {
|
||||
mm.empty(user, 'auth');
|
||||
request(app)
|
||||
.post('/_session')
|
||||
.send({
|
||||
name: 'name',
|
||||
password: '123'
|
||||
})
|
||||
.expect(401, done);
|
||||
});
|
||||
|
||||
it('should 200 auth pass by user.auth', function (done) {
|
||||
mm.data(user, 'auth', {name: 'name'});
|
||||
request(app)
|
||||
.post('/_session')
|
||||
.send({
|
||||
name: 'name',
|
||||
password: '123'
|
||||
})
|
||||
.expect(200)
|
||||
.expect({
|
||||
ok: true,
|
||||
name: 'name',
|
||||
roles: []
|
||||
}, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /-/user/:name/-rev/:rev', function () {
|
||||
it('should 404 when without a name', function (done) {
|
||||
request(app)
|
||||
@@ -171,19 +149,26 @@ describe('controllers/registry/user.test.js', function () {
|
||||
mm.error(user, 'update', 'mock error');
|
||||
request(app)
|
||||
.put('/-/user/org.couchdb.user:cnpmjstest10/-rev/:1-123')
|
||||
.send({
|
||||
name: 'cnpmjstest10',
|
||||
password: 'cnpmjstest10',
|
||||
email: 'cnpmjstest10@cnpmjs.org'
|
||||
})
|
||||
.set('authorization', 'basic ' + new Buffer('cnpmjstest10:cnpmjstest10').toString('base64'))
|
||||
.expect(500, done);
|
||||
});
|
||||
|
||||
it('should 409 when req.body.rev error', function (done) {
|
||||
it('should 201 when req.body.rev error', function (done) {
|
||||
request(app)
|
||||
.put('/-/user/org.couchdb.user:cnpmjstest10/-rev/:1-123')
|
||||
.set('authorization', 'basic ' + new Buffer('cnpmjstest10:cnpmjstest10').toString('base64'))
|
||||
.send({
|
||||
name: 'cnpmjstest10',
|
||||
password: 'cnpmjstest10',
|
||||
email: 'cnpmjstest10@cnpmjs.org',
|
||||
rev: '1-123'
|
||||
})
|
||||
.expect(409, done);
|
||||
.expect(201, done);
|
||||
});
|
||||
|
||||
it('should 201 update ok', function (done) {
|
||||
@@ -192,10 +177,192 @@ describe('controllers/registry/user.test.js', function () {
|
||||
.put('/-/user/org.couchdb.user:cnpmjstest10/-rev/:1-123')
|
||||
.set('authorization', 'basic ' + new Buffer('cnpmjstest10:cnpmjstest10').toString('base64'))
|
||||
.send({
|
||||
name: 'cnpmjstest10',
|
||||
password: 'cnpmjstest10',
|
||||
email: 'cnpmjstest10@cnpmjs.org',
|
||||
rev: '1-123'
|
||||
})
|
||||
.expect(201, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('config.customUserSerivce = true', function () {
|
||||
beforeEach(function () {
|
||||
mm(config, 'customUserService', true);
|
||||
});
|
||||
|
||||
it('should 422 when password missing', function (done) {
|
||||
request(app)
|
||||
.put('/-/user/org.couchdb.user:cnpmjstest10-not-exists')
|
||||
.send({
|
||||
name: 'cnpmjstest10-not-exists',
|
||||
password: '',
|
||||
email: 'cnpmjstest10@cnpmjs.org'
|
||||
})
|
||||
.expect({
|
||||
error: 'paramError',
|
||||
reason: 'params missing, name, email or password missing.'
|
||||
})
|
||||
.expect(422, done);
|
||||
});
|
||||
|
||||
it('should 201 login success', function (done) {
|
||||
request(app)
|
||||
.put('/-/user/org.couchdb.user:cnpmjstest10')
|
||||
.send({
|
||||
name: 'cnpmjstest10',
|
||||
password: 'cnpmjstest10',
|
||||
email: 'cnpmjstest10@cnpmjs.org'
|
||||
})
|
||||
.expect(201, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'id', 'rev');
|
||||
res.body.id.should.equal('org.couchdb.user:cnpmjstest10');
|
||||
res.body.rev.should.match(/\d+\-cnpmjstest10/);
|
||||
res.body.ok.should.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should 401 login fail', function (done) {
|
||||
request(app)
|
||||
.put('/-/user/org.couchdb.user:cnpmjstest10-not-exists')
|
||||
.send({
|
||||
name: 'cnpmjstest10-not-exists',
|
||||
password: 'cnpmjstest10',
|
||||
email: 'cnpmjstest10@cnpmjs.org'
|
||||
})
|
||||
.expect({
|
||||
error: 'unauthorized',
|
||||
reason: 'Login fail, please check your login name and password'
|
||||
})
|
||||
.expect(401, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('config.customUserService = true', function () {
|
||||
beforeEach(function () {
|
||||
mm(config, 'customUserService', true);
|
||||
});
|
||||
|
||||
afterEach(mm.restore);
|
||||
|
||||
it('should show custom user info: admin', function (done) {
|
||||
mm(UserService, 'get', function* () {
|
||||
return {
|
||||
login: 'mock_custom_user',
|
||||
email: 'mock_custom_user@cnpmjs.org',
|
||||
name: 'mock_custom_user fullname',
|
||||
avatar_url: 'avatar_url',
|
||||
html_url: 'html_url',
|
||||
im_url: '',
|
||||
site_admin: true,
|
||||
scopes: ['@test-user-scope']
|
||||
};
|
||||
});
|
||||
request(app)
|
||||
.get('/-/user/org.couchdb.user:mock_custom_user')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var user = res.body;
|
||||
delete user._cnpm_meta.gmt_create;
|
||||
delete user._cnpm_meta.gmt_modified;
|
||||
delete user._cnpm_meta.id;
|
||||
delete user.date;
|
||||
|
||||
user.should.eql({
|
||||
_id: 'org.couchdb.user:mock_custom_user',
|
||||
_rev: '1-mock_custom_user',
|
||||
name: 'mock_custom_user',
|
||||
email: 'mock_custom_user@cnpmjs.org',
|
||||
type: 'user',
|
||||
roles: [],
|
||||
// date: '2014-07-28T16:46:36.000Z',
|
||||
avatar: 'avatar_url',
|
||||
fullname: 'mock_custom_user fullname',
|
||||
homepage: 'html_url',
|
||||
_cnpm_meta:
|
||||
{
|
||||
// id: 4,
|
||||
npm_user: false,
|
||||
custom_user: true,
|
||||
// gmt_create: '2014-07-28T16:46:36.000Z',
|
||||
// gmt_modified: '2014-07-28T16:46:36.000Z',
|
||||
admin: true,
|
||||
scopes: [ '@test-user-scope' ] }
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should show custom user info: not admin', function (done) {
|
||||
mm(UserService, 'get', function* () {
|
||||
return {
|
||||
login: 'mock_custom_not_admin_user',
|
||||
email: 'mock_custom_not_admin_user@cnpmjs.org',
|
||||
name: 'mock_custom_not_admin_user fullname',
|
||||
avatar_url: 'avatar_url',
|
||||
html_url: 'html_url',
|
||||
im_url: '',
|
||||
site_admin: false,
|
||||
scopes: ['@test-user-scope']
|
||||
};
|
||||
});
|
||||
request(app)
|
||||
.get('/-/user/org.couchdb.user:mock_custom_not_admin_user')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var user = res.body;
|
||||
delete user._cnpm_meta.gmt_create;
|
||||
delete user._cnpm_meta.gmt_modified;
|
||||
delete user._cnpm_meta.id;
|
||||
delete user.date;
|
||||
|
||||
user.should.eql({
|
||||
_id: 'org.couchdb.user:mock_custom_not_admin_user',
|
||||
_rev: '1-mock_custom_not_admin_user',
|
||||
name: 'mock_custom_not_admin_user',
|
||||
email: 'mock_custom_not_admin_user@cnpmjs.org',
|
||||
type: 'user',
|
||||
roles: [],
|
||||
// date: '2014-07-28T16:46:36.000Z',
|
||||
avatar: 'avatar_url',
|
||||
fullname: 'mock_custom_not_admin_user fullname',
|
||||
homepage: 'html_url',
|
||||
_cnpm_meta:
|
||||
{
|
||||
// id: 5,
|
||||
npm_user: false,
|
||||
custom_user: true,
|
||||
// gmt_create: '2014-07-28T16:46:36.000Z',
|
||||
// gmt_modified: '2014-07-28T16:46:36.000Z',
|
||||
admin: false,
|
||||
scopes: [ '@test-user-scope' ] }
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should show error json when userSerive.auth throw error', function (done) {
|
||||
mm(UserService, 'auth', function* () {
|
||||
var err = new Error('mock user service auth error, please visit http://ooxx.net/user to sigup first');
|
||||
err.name = 'UserSeriveAuthError';
|
||||
err.status = 401;
|
||||
throw err;
|
||||
});
|
||||
|
||||
request(app)
|
||||
.put('/-/user/org.couchdb.user:cnpmjstest10')
|
||||
.send({
|
||||
name: 'cnpmjstest10',
|
||||
password: 'cnpmjstest10',
|
||||
email: 'cnpmjstest10@cnpmjs.org'
|
||||
})
|
||||
.expect({
|
||||
error: 'UserSeriveAuthError',
|
||||
reason: 'mock user service auth error, please visit http://ooxx.net/user to sigup first'
|
||||
})
|
||||
.expect(401, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - test/controllers/sync.test.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -17,18 +17,18 @@
|
||||
|
||||
var request = require('supertest');
|
||||
var should = require('should');
|
||||
var registryApp = require('../../servers/registry');
|
||||
var webApp = require('../../servers/web');
|
||||
var pedding = require('pedding');
|
||||
var mm = require('mm');
|
||||
var Npm = require('../../proxy/npm');
|
||||
var path = require('path');
|
||||
var Npm = require('../../proxy/npm');
|
||||
var registryApp = require('../../servers/registry');
|
||||
var webApp = require('../../servers/web');
|
||||
|
||||
describe('controllers/sync.test.js', function () {
|
||||
before(function (done) {
|
||||
done = pedding(2, done);
|
||||
registryApp.listen(0, done);
|
||||
webApp.listen(0, done);
|
||||
registryApp = registryApp.listen(0, done);
|
||||
webApp = webApp.listen(0, done);
|
||||
});
|
||||
|
||||
afterEach(mm.restore);
|
||||
@@ -43,36 +43,20 @@ describe('controllers/sync.test.js', function () {
|
||||
|
||||
it('should sync as publish success', function (done) {
|
||||
request(registryApp)
|
||||
.del('/utility/-rev/123')
|
||||
.del('/pedding/-rev/123')
|
||||
.set('authorization', baseauth)
|
||||
// .expect(200)
|
||||
// .expect({ok: true})
|
||||
.end(function (err, res) {
|
||||
should.not.exist(err);
|
||||
|
||||
mm.data(Npm, 'get', require(path.join(fixtures, 'utility.json')));
|
||||
request(registryApp)
|
||||
.put('/utility/sync?publish=true&nodeps=true')
|
||||
.put('/pedding/sync?publish=true&nodeps=true')
|
||||
.set('authorization', baseauth)
|
||||
.end(function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'logId');
|
||||
logIdRegistry = res.body.logId;
|
||||
done();
|
||||
// setTimeout(function () {
|
||||
// request(registryApp)
|
||||
// .get('/utility')
|
||||
// .expect(200)
|
||||
// .end(function (err, res) {
|
||||
// should.not.exist(err);
|
||||
// Object.keys(res.body.versions).length.should.above(0);
|
||||
// for (var v in res.body.versions) {
|
||||
// var pkg = res.body.versions[v];
|
||||
// pkg.should.have.property('_publish_on_cnpm', true);
|
||||
// }
|
||||
// done();
|
||||
// });
|
||||
// }, 5000);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -88,20 +72,10 @@ describe('controllers/sync.test.js', function () {
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should sync success', function (done) {
|
||||
it('should sync through web success', function (done) {
|
||||
mm.data(Npm, 'get', require(path.join(fixtures, 'utility.json')));
|
||||
done = pedding(2, done);
|
||||
request(registryApp)
|
||||
.put('/utility/sync')
|
||||
.set('authorization', baseauth)
|
||||
.end(function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'logId');
|
||||
logIdRegistry = res.body.logId;
|
||||
done();
|
||||
});
|
||||
request(webApp)
|
||||
.put('/sync/utility')
|
||||
.put('/sync/pedding')
|
||||
.end(function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'logId');
|
||||
@@ -110,10 +84,23 @@ describe('controllers/sync.test.js', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should sync through registry success', function (done) {
|
||||
mm.data(Npm, 'get', require(path.join(fixtures, 'utility.json')));
|
||||
request(registryApp)
|
||||
.put('/pedding/sync')
|
||||
.set('authorization', baseauth)
|
||||
.end(function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'logId');
|
||||
logIdRegistry = res.body.logId;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should get sync log', function (done) {
|
||||
done = pedding(2, done);
|
||||
request(registryApp)
|
||||
.get('/utility/sync/log/' + logIdRegistry)
|
||||
.get('/pedding/sync/log/' + logIdRegistry)
|
||||
.end(function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'log');
|
||||
@@ -121,7 +108,7 @@ describe('controllers/sync.test.js', function () {
|
||||
});
|
||||
|
||||
request(webApp)
|
||||
.get('/sync/utility/log/' + logIdWeb)
|
||||
.get('/sync/pedding/log/' + logIdWeb)
|
||||
.end(function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'log');
|
||||
@@ -129,4 +116,12 @@ describe('controllers/sync.test.js', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('scope package', function () {
|
||||
it('should sync scope package not found', function (done) {
|
||||
request(webApp)
|
||||
.put('/sync/@cnpm/not-exists-package')
|
||||
.expect(404, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,11 +27,6 @@ describe('controllers/total.test.js', function () {
|
||||
registryApp.listen(0, done);
|
||||
webApp.listen(0, done);
|
||||
});
|
||||
after(function (done) {
|
||||
done = pedding(2, done);
|
||||
registryApp.close(done);
|
||||
webApp.close(done);
|
||||
});
|
||||
|
||||
describe('GET / in registry', function () {
|
||||
it('should return total info', function (done) {
|
||||
|
||||
137
test/controllers/web/dist.test.js
Normal file
137
test/controllers/web/dist.test.js
Normal file
@@ -0,0 +1,137 @@
|
||||
/**!
|
||||
* cnpmjs.org - test/controllers/web/dist.test.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.cnpmjs.org)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var should = require('should');
|
||||
var request = require('supertest');
|
||||
var pedding = require('pedding');
|
||||
var mm = require('mm');
|
||||
var app = require('../../../servers/web');
|
||||
var Dist = require('../../../proxy/dist');
|
||||
|
||||
describe('controllers/web/dist.test.js', function () {
|
||||
before(function (done) {
|
||||
app = app.listen(0, done);
|
||||
});
|
||||
|
||||
afterEach(mm.restore);
|
||||
|
||||
describe('GET /dist/*', function (done) {
|
||||
it('should GET /dist redirect to /dist/', function (done) {
|
||||
request(app)
|
||||
.get('/dist')
|
||||
.expect(302)
|
||||
.expect('Location', '/dist/', done);
|
||||
});
|
||||
|
||||
it('should GET /dist/ show file list', function (done) {
|
||||
request(app)
|
||||
.get('/dist/')
|
||||
.expect('Content-Type', 'text/html; charset=utf-8')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.text.should.containEql('<title>Mirror index of http://nodejs.org/dist/</title>');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should mock return files and dirs', function (done) {
|
||||
mm(Dist, 'listdir', function* () {
|
||||
return [
|
||||
{name: 'ooxx/', date: '02-May-2014 00:54'},
|
||||
{name: 'foo.txt', size: 1024, date: '02-May-2014 00:54'},
|
||||
];
|
||||
});
|
||||
request(app)
|
||||
.get('/dist/v1.0.0/')
|
||||
.expect('Content-Type', 'text/html; charset=utf-8')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.text.should.containEql('<title>Mirror index of http://nodejs.org/dist/v1.0.0/</title>');
|
||||
res.text.should.containEql('<h1>Mirror index of <a target="_blank" href="http://nodejs.org/dist/v1.0.0/">http://nodejs.org/dist/v1.0.0/</a></h1>');
|
||||
res.text.should.containEql('<a href="ooxx/">ooxx/</a> 02-May-2014 00:54 -\n');
|
||||
res.text.should.containEql('<a href="foo.txt">foo.txt</a> 02-May-2014 00:54 1024\n');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should list files and dirs', function (done) {
|
||||
mm(Dist, 'listdir', function* () {
|
||||
return [
|
||||
{name: 'npm/', date: '02-May-2014 00:54'},
|
||||
{name: 'npm-versions.txt', size: 1676, date: '02-May-2014 00:54'},
|
||||
];
|
||||
});
|
||||
request(app)
|
||||
.get('/dist/')
|
||||
.expect('Content-Type', 'text/html; charset=utf-8')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.text.should.containEql('<title>Mirror index of http://nodejs.org/dist/</title>');
|
||||
res.text.should.containEql('<h1>Mirror index of <a target="_blank" href="http://nodejs.org/dist/">http://nodejs.org/dist/</a></h1>');
|
||||
res.text.should.containEql('<a href="npm/">npm/</a> 02-May-2014 00:54 -\n');
|
||||
res.text.should.containEql('<a href="npm-versions.txt">npm-versions.txt</a> 02-May-2014 00:54 1676\n');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /dist/ files', function () {
|
||||
it('should redirect to nfs url', function (done) {
|
||||
mm(Dist, 'getfile', function* () {
|
||||
return {
|
||||
name: 'foo.txt', size: 1024, date: '02-May-2014 00:54',
|
||||
url: 'http://mock.com/dist/v0.10.28/SHASUMS.txt'
|
||||
};
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/dist/v0.10.28/SHASUMS.txt')
|
||||
.expect(302)
|
||||
.expect('Location', 'http://mock.com/dist/v0.10.28/SHASUMS.txt', done);
|
||||
});
|
||||
|
||||
it('should GET /dist/npm-versions.txt redirect to nfs url', function (done) {
|
||||
mm(Dist, 'getfile', function* (fullname) {
|
||||
fullname.should.equal('/npm-versions.txt');
|
||||
return {
|
||||
name: 'npm-versions.txt', size: 1024, date: '02-May-2014 00:54',
|
||||
url: 'http://mock.com/dist/npm-versions.txt'
|
||||
};
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/dist/npm-versions.txt')
|
||||
.expect(302)
|
||||
.expect('Location', 'http://mock.com/dist/npm-versions.txt', done);
|
||||
});
|
||||
|
||||
it('should download nfs file and send it', function (done) {
|
||||
mm(Dist, 'getfile', function* () {
|
||||
return {
|
||||
name: 'foo.txt',
|
||||
size: 1264,
|
||||
date: '02-May-2014 00:54',
|
||||
url: '/dist/v0.10.28/SHASUMS.txt'
|
||||
};
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/dist/v0.10.28/SHASUMS.txt')
|
||||
.expect(200)
|
||||
.expect(/6eff580cc8460741155d42ef1ef537961194443f/, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -17,24 +17,41 @@
|
||||
var should = require('should');
|
||||
var request = require('supertest');
|
||||
var mm = require('mm');
|
||||
var path = require('path');
|
||||
var pedding = require('pedding');
|
||||
var mysql = require('../../../common/mysql');
|
||||
var app = require('../../../servers/web');
|
||||
var registry = require('../../../servers/registry');
|
||||
var pkg = require('../../../controllers/web/package');
|
||||
var SyncModuleWorker = require('../../../proxy/sync_module_worker');
|
||||
var utils = require('../../utils');
|
||||
var config = require('../../../config');
|
||||
|
||||
var fixtures = path.join(path.dirname(path.dirname(__dirname)), 'fixtures');
|
||||
|
||||
describe('controllers/web/package.test.js', function () {
|
||||
before(function (done) {
|
||||
app.listen(0, done);
|
||||
});
|
||||
after(function (done) {
|
||||
app.close(done);
|
||||
done = pedding(2, done);
|
||||
registry = registry.listen(0, function () {
|
||||
// name: mk2testmodule
|
||||
var pkg = utils.getPackage('mk2testmodule', '0.0.1', utils.admin);
|
||||
request(registry)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send(pkg)
|
||||
.end(done);
|
||||
});
|
||||
|
||||
app = app.listen(0, done);
|
||||
});
|
||||
|
||||
afterEach(mm.restore);
|
||||
|
||||
describe('GET /_list/search/search', function () {
|
||||
it('should search with "c"', function (done) {
|
||||
it('should search with "m"', function (done) {
|
||||
request(app)
|
||||
.get('/_list/search/search?startkey="c"&limit=2')
|
||||
.get('/_list/search/search?startkey="m"&limit=2')
|
||||
.expect('content-type', 'application/json; charset=utf-8')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('rows');
|
||||
@@ -47,9 +64,9 @@ describe('controllers/web/package.test.js', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should search with c', function (done) {
|
||||
it('should search with m', function (done) {
|
||||
request(app)
|
||||
.get('/_list/search/search?startkey=c&limit=2')
|
||||
.get('/_list/search/search?startkey=m&limit=2')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('rows');
|
||||
@@ -76,11 +93,18 @@ describe('controllers/web/package.test.js', function () {
|
||||
describe('GET /package/:name', function (done) {
|
||||
it('should get 200', function (done) {
|
||||
request(app)
|
||||
.get('/package/cutter')
|
||||
.get('/package/mk2testmodule')
|
||||
.expect(200)
|
||||
// .expect('content-encoding', 'gzip')
|
||||
.expect('content-type', 'text/html; charset=utf-8')
|
||||
.expect(/<div id="package">/)
|
||||
.expect(/<th>Maintainers<\/th>/)
|
||||
.expect(/<th>Version<\/th>/, done);
|
||||
.expect(/<th>Version<\/th>/, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.should.have.header('etag');
|
||||
res.text.should.containEql('<meta charset="utf-8">');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should get 404', function (done) {
|
||||
@@ -93,7 +117,7 @@ describe('controllers/web/package.test.js', function () {
|
||||
describe('GET /package/:name/:version', function (done) {
|
||||
it('should 200 when get by version', function (done) {
|
||||
request(app)
|
||||
.get('/package/cutter/0.0.2')
|
||||
.get('/package/mk2testmodule/0.0.1')
|
||||
.expect(200)
|
||||
.expect(/<div id="package">/)
|
||||
.expect(/<th>Maintainers<\/th>/)
|
||||
@@ -102,21 +126,22 @@ describe('controllers/web/package.test.js', function () {
|
||||
|
||||
it('should 200 when get by tag', function (done) {
|
||||
request(app)
|
||||
.get('/package/cutter/latest')
|
||||
.get('/package/mk2testmodule/latest')
|
||||
.expect(200)
|
||||
.expect(/<div id="package">/)
|
||||
.expect(/<th>Maintainers<\/th>/)
|
||||
.expect(/<th>Version<\/th>/, done);
|
||||
});
|
||||
|
||||
it('should 404 when get by version not exist', function (done) {
|
||||
request(app)
|
||||
.get('/package/cutter/1.1.2')
|
||||
.get('/package/mk2testmodule/1.1.2')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should 404 when get by tag', function (done) {
|
||||
request(app)
|
||||
.get('/package/cutter/notexisttag')
|
||||
.get('/package/mk2testmodule/notexisttag')
|
||||
.expect(404, done);
|
||||
});
|
||||
});
|
||||
@@ -124,14 +149,14 @@ describe('controllers/web/package.test.js', function () {
|
||||
describe('GET /browse/keyword/:word', function () {
|
||||
it('should list by keyword ok', function (done) {
|
||||
request(app)
|
||||
.get('/browse/keyword/cnpm')
|
||||
.get('/browse/keyword/mk2testmodule')
|
||||
.expect(200)
|
||||
.expect(/Packages match/, done);
|
||||
});
|
||||
|
||||
it('should list by keyword with json ok', function (done) {
|
||||
request(app)
|
||||
.get('/browse/keyword/cnpm?type=json')
|
||||
.get('/browse/keyword/mk2testmodule?type=json')
|
||||
.expect(200)
|
||||
.expect('content-type', 'application/json; charset=utf-8', done);
|
||||
});
|
||||
@@ -162,7 +187,7 @@ describe('controllers/web/package.test.js', function () {
|
||||
url: 'http://opensource.org/licenses/MIT'
|
||||
});
|
||||
|
||||
var p = {license: ['http://foo/MIT']};
|
||||
p = {license: ['http://foo/MIT']};
|
||||
pkg.setLicense(p);
|
||||
p.license.should.have.keys('name', 'url');
|
||||
p.license.should.eql({
|
||||
@@ -170,7 +195,7 @@ describe('controllers/web/package.test.js', function () {
|
||||
url: 'http://foo/MIT'
|
||||
});
|
||||
|
||||
var p = {license: {name: 'mit', url: 'http://foo/mit'}};
|
||||
p = {license: {name: 'mit', url: 'http://foo/mit'}};
|
||||
pkg.setLicense(p);
|
||||
p.license.should.have.keys('name', 'url');
|
||||
p.license.should.eql({
|
||||
@@ -189,4 +214,45 @@ describe('controllers/web/package.test.js', function () {
|
||||
.expect(/Log/, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unpublished package', function () {
|
||||
before(function (done) {
|
||||
var worker = new SyncModuleWorker({
|
||||
name: ['tnpm'],
|
||||
username: 'fengmk2'
|
||||
});
|
||||
|
||||
worker.start();
|
||||
worker.on('end', function () {
|
||||
var names = worker.successes.concat(worker.fails);
|
||||
names.sort();
|
||||
names.should.eql(['tnpm']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should display unpublished info', function (done) {
|
||||
request(app)
|
||||
.get('/package/tnpm')
|
||||
.expect(200)
|
||||
.expect(/This package has been unpublished\./, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /privates', function () {
|
||||
it('should response no private packages', function (done) {
|
||||
mm(config, 'scopes', []);
|
||||
request(app)
|
||||
.get('/privates')
|
||||
.expect(/Can not found private package/)
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it('should response no private packages', function (done) {
|
||||
request(app)
|
||||
.get('/privates')
|
||||
.expect(/Private packages in this registry/)
|
||||
.expect(200, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
177
test/controllers/web/package/scope_package.test.js
Normal file
177
test/controllers/web/package/scope_package.test.js
Normal file
@@ -0,0 +1,177 @@
|
||||
/**!
|
||||
* cnpmjs.org - test/controllers/web/package/scope_package.test.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var should = require('should');
|
||||
var request = require('supertest');
|
||||
var pedding = require('pedding');
|
||||
var mm = require('mm');
|
||||
var config = require('../../../../config');
|
||||
var registry = require('../../../../servers/registry');
|
||||
var web = require('../../../../servers/web');
|
||||
var utils = require('../../../utils');
|
||||
var Module = require('../../../../proxy/module');
|
||||
|
||||
describe('controllers/web/package/scope_package.test.js', function () {
|
||||
var pkgname = '@cnpm/test-web-scope-package';
|
||||
var pkgURL = '/@' + encodeURIComponent(pkgname.substring(1));
|
||||
before(function (done) {
|
||||
done = pedding(2, done);
|
||||
registry = registry.listen(0, function () {
|
||||
// add scope package
|
||||
var pkg = utils.getPackage(pkgname, '0.0.1', utils.admin);
|
||||
request(registry)
|
||||
.put(pkgURL)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send(pkg)
|
||||
.expect(201, function (err) {
|
||||
should.not.exist(err);
|
||||
pkg = utils.getPackage(pkgname, '0.0.2', utils.admin);
|
||||
// publish 0.0.2
|
||||
request(registry)
|
||||
.put(pkgURL)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send(pkg)
|
||||
.expect(201, done);
|
||||
});
|
||||
});
|
||||
web = web.listen(0, done);
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
mm(config, 'scopes', ['@cnpm', '@cnpmtest']);
|
||||
});
|
||||
|
||||
afterEach(mm.restore);
|
||||
|
||||
it('should show scope package info page: /@scope%2Fname', function (done) {
|
||||
request(web)
|
||||
.get('/package' + pkgURL)
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var body = res.text;
|
||||
body.should.containEql('$ cnpm install @cnpm/test-web-scope-package');
|
||||
body.should.containEql('/@cnpm/test-web-scope-package/download/@cnpm/test-web-scope-package-0.0.2.tgz');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should show scope package info page: encodeURIComponent("/@scope/name")', function (done) {
|
||||
request(web)
|
||||
.get('/package/' + encodeURIComponent(pkgname))
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var body = res.text;
|
||||
body.should.containEql('$ cnpm install @cnpm/test-web-scope-package');
|
||||
body.should.containEql('/@cnpm/test-web-scope-package/download/@cnpm/test-web-scope-package-0.0.2.tgz');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should show scope package info page: /@scope/name', function (done) {
|
||||
request(web)
|
||||
.get('/package/' + pkgname)
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var body = res.text;
|
||||
body.should.containEql('$ cnpm install @cnpm/test-web-scope-package');
|
||||
body.should.containEql('/@cnpm/test-web-scope-package/download/@cnpm/test-web-scope-package-0.0.2.tgz');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should /package/@scope/name/ 404', function (done) {
|
||||
request(web)
|
||||
.get('/package/' + pkgname + '/')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should show scope package with version: /@scope/name/0.0.2', function (done) {
|
||||
request(web)
|
||||
.get('/package/' + pkgname + '/0.0.2')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var body = res.text;
|
||||
body.should.containEql('$ cnpm install @cnpm/test-web-scope-package');
|
||||
body.should.containEql('/@cnpm/test-web-scope-package/download/@cnpm/test-web-scope-package-0.0.2.tgz');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should /@scope/name redirect to /package/@scope/name', function (done) {
|
||||
request(web)
|
||||
.get('/' + pkgname)
|
||||
.expect('Location', '/package/' + pkgname)
|
||||
.expect(302, done);
|
||||
});
|
||||
|
||||
describe('support adapt scope', function () {
|
||||
before(function (done) {
|
||||
var pkg = utils.getPackage('test-default-web-scope-package', '0.0.1', utils.admin);
|
||||
request(registry)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send(pkg)
|
||||
.expect(201, done);
|
||||
});
|
||||
|
||||
it('should adapt /@cnpm/test-default-web-scope-package => /test-default-web-scope-package', function (done) {
|
||||
mm(config, 'adaptScope', true);
|
||||
request(web)
|
||||
.get('/package/@cnpm/test-default-web-scope-package')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
var body = res.text;
|
||||
body.should.containEql('@cnpm/test-default-web-scope-package');
|
||||
body.should.containEql('/test-default-web-scope-package/download/test-default-web-scope-package-0.0.1.tgz');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not adapt /@cnpm123/test-default-web-scope-package', function (done) {
|
||||
mm(config, 'adaptScope', true);
|
||||
request(web)
|
||||
.get('/package/@cnpm123/test-default-web-scope-package')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should not adapt', function (done) {
|
||||
mm(config, 'adaptScope', false);
|
||||
request(web)
|
||||
.get('/package/@cnpm/test-default-web-scope-package')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should 404 when pkg not exists', function (done) {
|
||||
mm(config, 'adaptScope', true);
|
||||
request(web)
|
||||
.get('/package/@cnpm/test-default-web-scope-package-not-exists')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should 404 when pkg is not private package', function (done) {
|
||||
var getByTag = Module.getByTag;
|
||||
mm(Module, 'getByTag', function* (name, tag) {
|
||||
var pkg = yield getByTag.call(Module, name, tag);
|
||||
pkg && delete pkg.package._publish_on_cnpm;
|
||||
return pkg;
|
||||
});
|
||||
mm(config, 'adaptScope', true);
|
||||
request(web)
|
||||
.get('/package/@cnpm/test-default-web-scope-package')
|
||||
.expect(404, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -20,19 +20,17 @@ var app = require('../../../servers/web');
|
||||
|
||||
describe('controllers/web/user.test.js', function () {
|
||||
before(function (done) {
|
||||
app.listen(0, done);
|
||||
});
|
||||
after(function (done) {
|
||||
app.close(done);
|
||||
app = app.listen(0, done);
|
||||
});
|
||||
|
||||
describe('GET /~:name', function (done) {
|
||||
it('should get 200', function (done) {
|
||||
request(app)
|
||||
.get('/~dead_horse')
|
||||
.get('/~cnpmjstest10')
|
||||
.expect(200)
|
||||
.expect('content-type', 'text/html; charset=utf-8')
|
||||
.expect(/<div id="profile">/)
|
||||
.expect(/Packages by /, done);
|
||||
.expect(/Packages by/, done);
|
||||
});
|
||||
|
||||
it('should get 404', function (done) {
|
||||
@@ -43,7 +41,7 @@ describe('controllers/web/user.test.js', function () {
|
||||
|
||||
it('should get not eixst user but have modules 200', function (done) {
|
||||
request(app)
|
||||
.get('/~tjholowaychuk')
|
||||
.get('/~cnpmjstest101')
|
||||
.expect(200)
|
||||
.expect(/<div id="profile">/)
|
||||
.expect(/Packages by/, done);
|
||||
|
||||
1
test/fixtures/cnpmtest-package.json
vendored
Normal file
1
test/fixtures/cnpmtest-package.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"_id":"cnpmtest-package","_rev":"382002","name":"cnpmtest-package","description":"cnpmtest-package","dist-tags":{"latest":"0.0.0"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"time":{"modified":"2014-04-09T10:54:45.000Z","created":"2014-04-09T10:54:39.425Z","0.0.0":"2014-04-09T10:54:39.425Z"},"users":{},"author":{"name":"fengmk2","email":"fengmk2@gmail.com","url":"http://fengmk2.github.com"},"repository":{"type":"git","url":"git://github.com/{{group}}/cnpmtest-package.git","web":"https://github.com/{{group}}/cnpmtest-package"},"versions":{"0.0.0":{"name":"cnpmtest-package","version":"0.0.0","description":"cnpmtest-package","main":"index.js","scripts":{"test":"make test-all"},"config":{"cov":{"threshold":100}},"dependencies":{},"devDependencies":{"autod":"*","contributors":"*","should":"*","jshint":"*","cov":"*","istanbul-harmony":"*","mocha":"*"},"homepage":"https://github.com/{{group}}/cnpmtest-package","repository":{"type":"git","url":"git://github.com/{{group}}/cnpmtest-package.git","web":"https://github.com/{{group}}/cnpmtest-package"},"bugs":{"url":"https://github.com/{{group}}/cnpmtest-package/issues","email":"fengmk2@gmail.com"},"keywords":["cnpmtest-package"],"engines":{"node":">= 0.10.0"},"author":{"name":"fengmk2","email":"fengmk2@gmail.com","url":"http://fengmk2.github.com"},"license":"MIT","contributors":[],"_id":"cnpmtest-package@0.0.0","dist":{"shasum":"81ea57c24a7f9f6e7263385116fc433f0ad9e179","size":1839,"noattachment":false,"tarball":"http://r.cnpmjs.org/cnpmtest-package/download/cnpmtest-package-0.0.0.tgz"},"_from":".","_npmVersion":"1.4.6","_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"directories":{},"publish_time":1397040879425,"_cnpm_publish_time":1397040879425}},"readme":"cnpmtest-package\n=======\n\n[](http://travis-ci.org/{{group}}/cnpmtest-package)\n[](https://gemnasium.com/{{group}}/cnpmtest-package)\n\n[](https://nodei.co/npm/cnpmtest-package/)\n\n\n\ncnpmtest-package desc\n\n## Install\n\n```bash\n$ npm install cnpmtest-package\n```\n\n## Usage\n\n```js\nvar cnpmtest-package = require('cnpmtest-package');\n\ncnpmtest-package.foo(function (err) {\n\n});\n```\n\n## License\n\n(The MIT License)\n\nCopyright (c) 2014 fengmk2 <fengmk2@gmail.com> and other contributors\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_attachments":{},"homepage":"https://github.com/{{group}}/cnpmtest-package","bugs":{"url":"https://github.com/{{group}}/cnpmtest-package/issues","email":"fengmk2@gmail.com"},"license":"MIT"}
|
||||
1
test/fixtures/fengmk2.json
vendored
Normal file
1
test/fixtures/fengmk2.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"_id":"org.couchdb.user:fengmk2","_rev":"25-ba29aa975022944b4ba547ab11ce3af5","name":"fengmk2","email":"fengmk2@gmail.com","type":"user","roles":[],"date":"2014-03-15T08:06:18.870Z","fullname":"fengmk2","avatar":"https://secure.gravatar.com/avatar/95b9d41231617a05ced5604d242c9670?s=50&d=retro","freenode":"","github":"fengmk2","homepage":"http://fengmk2.github.com","twitter":"fengmk2","avatarMedium":"https://secure.gravatar.com/avatar/95b9d41231617a05ced5604d242c9670?s=100&d=retro","avatarLarge":"https://secure.gravatar.com/avatar/95b9d41231617a05ced5604d242c9670?s=496&d=retro","fields":[{"name":"fullname","value":"fengmk2","title":"Full Name","show":"fengmk2"},{"name":"email","value":"fengmk2@gmail.com","title":"Email","show":"<a href=\"mailto:fengmk2@gmail.com\">fengmk2@gmail.com</a>"},{"name":"github","value":"fengmk2","title":"Github","show":"<a rel=\"me\" href=\"https://github.com/fengmk2\">fengmk2</a>"},{"name":"twitter","value":"fengmk2","title":"Twitter","show":"<a rel=\"me\" href=\"https://twitter.com/fengmk2\">@fengmk2</a>"},{"name":"appdotnet","value":"","title":"App.net","show":""},{"name":"homepage","value":"http://fengmk2.github.com","title":"Homepage","show":"<a rel=\"me\" href=\"http://fengmk2.github.com/\">http://fengmk2.github.com</a>"},{"name":"freenode","value":"","title":"IRC Handle","show":""}],"appdotnet":"fengmk2"}
|
||||
16
test/fixtures/scope-package/.gitignore
vendored
Normal file
16
test/fixtures/scope-package/.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
coverage.html
|
||||
*.seed
|
||||
*.log
|
||||
*.csv
|
||||
*.dat
|
||||
*.out
|
||||
*.pid
|
||||
*.gz
|
||||
|
||||
pids
|
||||
logs
|
||||
results
|
||||
|
||||
node_modules
|
||||
npm-debug.log
|
||||
coverage/
|
||||
4
test/fixtures/scope-package/.jshintignore
vendored
Normal file
4
test/fixtures/scope-package/.jshintignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
coverage/
|
||||
.tmp/
|
||||
.git/
|
||||
95
test/fixtures/scope-package/.jshintrc
vendored
Normal file
95
test/fixtures/scope-package/.jshintrc
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
{
|
||||
// JSHint Default Configuration File (as on JSHint website)
|
||||
// See http://jshint.com/docs/ for more details
|
||||
|
||||
"maxerr" : 50, // {int} Maximum error before stopping
|
||||
|
||||
// Enforcing
|
||||
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
|
||||
"camelcase" : false, // true: Identifiers must be in camelCase
|
||||
"curly" : true, // true: Require {} for every new block or scope
|
||||
"eqeqeq" : true, // true: Require triple equals (===) for comparison
|
||||
"forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty()
|
||||
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
|
||||
"indent" : false, // {int} Number of spaces to use for indentation
|
||||
"latedef" : false, // true: Require variables/functions to be defined before being used
|
||||
"newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
|
||||
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
|
||||
"noempty" : true, // true: Prohibit use of empty blocks
|
||||
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
|
||||
"plusplus" : false, // true: Prohibit use of `++` & `--`
|
||||
"quotmark" : false, // Quotation mark consistency:
|
||||
// false : do nothing (default)
|
||||
// true : ensure whatever is used is consistent
|
||||
// "single" : require single quotes
|
||||
// "double" : require double quotes
|
||||
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
|
||||
"unused" : false, // true: Require all defined variables be used
|
||||
"strict" : true, // true: Requires all functions run in ES5 Strict Mode
|
||||
"trailing" : false, // true: Prohibit trailing whitespaces
|
||||
"maxparams" : false, // {int} Max number of formal params allowed per function
|
||||
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
|
||||
"maxstatements" : false, // {int} Max number statements per function
|
||||
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
|
||||
"maxlen" : false, // {int} Max number of characters per line
|
||||
|
||||
// Relaxing
|
||||
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
|
||||
"boss" : true, // true: Tolerate assignments where comparisons would be expected
|
||||
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
|
||||
"eqnull" : false, // true: Tolerate use of `== null`
|
||||
"es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
|
||||
"esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`)
|
||||
"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
|
||||
// (ex: `for each`, multiple try/catch, function expression…)
|
||||
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
|
||||
"expr" : true, // true: Tolerate `ExpressionStatement` as Programs
|
||||
"funcscope" : false, // true: Tolerate defining variables inside control statements"
|
||||
"globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
|
||||
"iterator" : false, // true: Tolerate using the `__iterator__` property
|
||||
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
|
||||
"laxbreak" : true, // true: Tolerate possibly unsafe line breakings
|
||||
"laxcomma" : false, // true: Tolerate comma-first style coding
|
||||
"loopfunc" : false, // true: Tolerate functions being defined in loops
|
||||
"multistr" : true, // true: Tolerate multi-line strings
|
||||
"proto" : false, // true: Tolerate using the `__proto__` property
|
||||
"scripturl" : false, // true: Tolerate script-targeted URLs
|
||||
"smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment
|
||||
"shadow" : true, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
|
||||
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
|
||||
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
|
||||
"validthis" : false, // true: Tolerate using this in a non-constructor function
|
||||
|
||||
// Environments
|
||||
"browser" : true, // Web Browser (window, document, etc)
|
||||
"couch" : false, // CouchDB
|
||||
"devel" : true, // Development/debugging (alert, confirm, etc)
|
||||
"dojo" : false, // Dojo Toolkit
|
||||
"jquery" : false, // jQuery
|
||||
"mootools" : false, // MooTools
|
||||
"node" : true, // Node.js
|
||||
"nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
|
||||
"prototypejs" : false, // Prototype and Scriptaculous
|
||||
"rhino" : false, // Rhino
|
||||
"worker" : false, // Web Workers
|
||||
"wsh" : false, // Windows Scripting Host
|
||||
"yui" : false, // Yahoo User Interface
|
||||
"noyield" : true, // allow generators without a yield
|
||||
|
||||
// Legacy
|
||||
"nomen" : false, // true: Prohibit dangling `_` in variables
|
||||
"onevar" : false, // true: Allow only one `var` statement per function
|
||||
"passfail" : false, // true: Stop on first error
|
||||
"white" : false, // true: Check against strict whitespace and indentation rules
|
||||
|
||||
// Custom Globals
|
||||
"globals" : { // additional predefined global variables
|
||||
// mocha
|
||||
"describe": true,
|
||||
"it": true,
|
||||
"before": true,
|
||||
"afterEach": true,
|
||||
"beforeEach": true,
|
||||
"after": true
|
||||
}
|
||||
}
|
||||
8
test/fixtures/scope-package/.npmignore
vendored
Normal file
8
test/fixtures/scope-package/.npmignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
test/
|
||||
Makefile
|
||||
.travis.yml
|
||||
logo.png
|
||||
.jshintignore
|
||||
.jshintrc
|
||||
.gitingore
|
||||
coverage/
|
||||
6
test/fixtures/scope-package/.travis.yml
vendored
Normal file
6
test/fixtures/scope-package/.travis.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- '0.11'
|
||||
- '0.10'
|
||||
script: "make test-travis"
|
||||
after_script: "npm install coveralls@2 && cat ./coverage/lcov.info | coveralls"
|
||||
0
test/fixtures/scope-package/AUTHORS
vendored
Normal file
0
test/fixtures/scope-package/AUTHORS
vendored
Normal file
21
test/fixtures/scope-package/LICENSE.txt
vendored
Normal file
21
test/fixtures/scope-package/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
This software is licensed under the MIT License.
|
||||
|
||||
Copyright (c) 2014 fengmk2 <fengmk2@gmail.com> and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
51
test/fixtures/scope-package/Makefile
vendored
Normal file
51
test/fixtures/scope-package/Makefile
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
TESTS = test/*.test.js
|
||||
REPORTER = spec
|
||||
TIMEOUT = 1000
|
||||
MOCHA_OPTS =
|
||||
|
||||
install:
|
||||
@npm install --registry=https://registry.npm.taobao.org --disturl=https://npm.taobao.org/dist
|
||||
|
||||
jshint: install
|
||||
@./node_modules/.bin/jshint .
|
||||
|
||||
test: install
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--harmony \
|
||||
--reporter $(REPORTER) \
|
||||
--timeout $(TIMEOUT) \
|
||||
$(MOCHA_OPTS) \
|
||||
$(TESTS)
|
||||
|
||||
test-cov cov: install
|
||||
@NODE_ENV=test node --harmony \
|
||||
node_modules/.bin/istanbul cover --preserve-comments \
|
||||
./node_modules/.bin/_mocha \
|
||||
-- \
|
||||
--reporter $(REPORTER) \
|
||||
--timeout $(TIMEOUT) \
|
||||
$(MOCHA_OPTS) \
|
||||
$(TESTS)
|
||||
@./node_modules/.bin/cov coverage
|
||||
|
||||
test-travis: install
|
||||
@NODE_ENV=test node --harmony \
|
||||
node_modules/.bin/istanbul cover --preserve-comments \
|
||||
./node_modules/.bin/_mocha \
|
||||
--report lcovonly \
|
||||
-- \
|
||||
--reporter dot \
|
||||
--timeout $(TIMEOUT) \
|
||||
$(MOCHA_OPTS) \
|
||||
$(TESTS)
|
||||
|
||||
test-all: install jshint test cov
|
||||
|
||||
autod: install
|
||||
@./node_modules/.bin/autod -w --prefix "~"
|
||||
@$(MAKE) install
|
||||
|
||||
contributors: install
|
||||
@./node_modules/.bin/contributors -f plain -o AUTHORS
|
||||
|
||||
.PHONY: test
|
||||
64
test/fixtures/scope-package/README.md
vendored
Normal file
64
test/fixtures/scope-package/README.md
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
scope-package
|
||||
=======
|
||||
|
||||
[![NPM version][npm-image]][npm-url]
|
||||
[![build status][travis-image]][travis-url]
|
||||
[![Test coverage][coveralls-image]][coveralls-url]
|
||||
[![Gittip][gittip-image]][gittip-url]
|
||||
[![David deps][david-image]][david-url]
|
||||
|
||||
[npm-image]: https://img.shields.io/npm/v/scope-package.svg?style=flat
|
||||
[npm-url]: https://npmjs.org/package/scope-package
|
||||
[travis-image]: https://img.shields.io/travis/node-modules/scope-package.svg?style=flat
|
||||
[travis-url]: https://travis-ci.org/node-modules/scope-package
|
||||
[coveralls-image]: https://img.shields.io/coveralls/node-modules/scope-package.svg?style=flat
|
||||
[coveralls-url]: https://coveralls.io/r/node-modules/scope-package?branch=master
|
||||
[gittip-image]: https://img.shields.io/gittip/fengmk2.svg?style=flat
|
||||
[gittip-url]: https://www.gittip.com/fengmk2/
|
||||
[david-image]: https://img.shields.io/david/node-modules/scope-package.svg?style=flat
|
||||
[david-url]: https://david-dm.org/node-modules/scope-package
|
||||
|
||||

|
||||
|
||||
scope-package desc
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
$ npm install scope-package
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
var scope-package = require('scope-package');
|
||||
|
||||
scope-package.foo(function (err) {
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2014 fengmk2 <fengmk2@gmail.com> and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
1
test/fixtures/scope-package/index.js
vendored
Normal file
1
test/fixtures/scope-package/index.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = require('./lib/scope-package');
|
||||
17
test/fixtures/scope-package/lib/scope-package.js
vendored
Normal file
17
test/fixtures/scope-package/lib/scope-package.js
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/**!
|
||||
* scope-package - lib/scope-package.js
|
||||
*
|
||||
* Copyright(c) 2014 fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var koa = require('koa');
|
||||
44
test/fixtures/scope-package/package.json
vendored
Normal file
44
test/fixtures/scope-package/package.json
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "@cnpm/scope-package",
|
||||
"version": "0.0.1",
|
||||
"description": "scope-package",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "make test-all"
|
||||
},
|
||||
"config": {
|
||||
"cov": {
|
||||
"threshold": 100
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
"devDependencies": {
|
||||
"autod": "*",
|
||||
"contributors": "*",
|
||||
"should": "*",
|
||||
"jshint": "*",
|
||||
"cov": "*",
|
||||
"istanbul-harmony": "*",
|
||||
"mocha": "*"
|
||||
},
|
||||
"homepage": "https://github.com/node-modules/scope-package",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/node-modules/scope-package.git",
|
||||
"web": "https://github.com/node-modules/scope-package"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/node-modules/scope-package/issues",
|
||||
"email": "fengmk2@gmail.com"
|
||||
},
|
||||
"keywords": [
|
||||
"scope-package"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"author": "fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)",
|
||||
"license": "MIT"
|
||||
}
|
||||
42
test/init.js
Normal file
42
test/init.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/**!
|
||||
* cnpmjs.org - test/init.js
|
||||
*
|
||||
* Copyright(c) fengmk2 and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var crypto = require('crypto');
|
||||
var utility = require('utility');
|
||||
var User = require('../proxy/user');
|
||||
|
||||
var usernames = [
|
||||
'cnpmjstest101',
|
||||
'cnpmjstest102',
|
||||
'cnpmjstest10'
|
||||
];
|
||||
|
||||
usernames.forEach(function (name) {
|
||||
var user = {
|
||||
name: name,
|
||||
email: 'fengmk2@gmail.com',
|
||||
// password: 'cnpmjstest10',
|
||||
ip: '127.0.0.1'
|
||||
};
|
||||
user.salt = crypto.randomBytes(30).toString('hex');
|
||||
user.password_sha = utility.sha1(user.name + user.salt);
|
||||
|
||||
User.add(user, function (err, result) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -20,6 +20,8 @@ var request = require('supertest');
|
||||
var app = require('../../servers/registry');
|
||||
var mm = require('mm');
|
||||
var mysql = require('../../common/mysql');
|
||||
var config = require('../../config');
|
||||
var UserService = require('../../services/user');
|
||||
|
||||
describe('middleware/auth.test.js', function () {
|
||||
before(function (done) {
|
||||
@@ -34,7 +36,7 @@ describe('middleware/auth.test.js', function () {
|
||||
describe('auth()', function () {
|
||||
it('should pass if no authorization', function (done) {
|
||||
request(app)
|
||||
.get('/-/user/org.couchdb.user:cnpmjstest1')
|
||||
.get('/-/user/org.couchdb.user:cnpmjstest10')
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
@@ -60,5 +62,29 @@ describe('middleware/auth.test.js', function () {
|
||||
.set('authorization', 'basic ' + new Buffer('cnpmjstest10:cnpmjstest10').toString('base64'))
|
||||
.expect(500, done);
|
||||
});
|
||||
|
||||
describe('config.customUserService = true', function () {
|
||||
beforeEach(function () {
|
||||
mm(config, 'customUserService', true);
|
||||
});
|
||||
|
||||
it('should 401 when user service auth throw error', function (done) {
|
||||
mm(UserService, 'auth', function* () {
|
||||
var err = new Error('mock user service auth error, please visit http://ooxx.net/user to sigup first');
|
||||
err.name = 'UserSeriveAuthError';
|
||||
err.status = 401;
|
||||
throw err;
|
||||
});
|
||||
|
||||
request(app)
|
||||
.put('/-/user/org.couchdb.user:cnpmjstest10/-rev/1')
|
||||
.set('authorization', 'basic ' + new Buffer('cnpmjstest10:cnpmjstest10').toString('base64'))
|
||||
.expect({
|
||||
error: 'UserSeriveAuthError',
|
||||
reason: 'mock user service auth error, please visit http://ooxx.net/user to sigup first'
|
||||
})
|
||||
.expect(401, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user