Compare commits
426 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
4e6eb0a9cc | ||
|
|
29f17dd5d1 | ||
|
|
3a48637ef1 | ||
|
|
e1029b005f | ||
|
|
66771dfc3b | ||
|
|
66f05a2f07 | ||
|
|
6660cabbb6 | ||
|
|
fbe1971957 | ||
|
|
d75c3877bb | ||
|
|
564ec488ea | ||
|
|
1559c16c3d | ||
|
|
2345536cbd | ||
|
|
89808e398b | ||
|
|
5ddf238c08 | ||
|
|
1ae193e306 | ||
|
|
9d511c326c | ||
|
|
78d7a77b0d | ||
|
|
0f494822bc | ||
|
|
211df84514 | ||
|
|
fee243726e | ||
|
|
52e7e6d069 | ||
|
|
551ae832e3 | ||
|
|
9af99f4af2 | ||
|
|
3e8ecda9e4 | ||
|
|
fb744176f8 | ||
|
|
5e1ab4356d | ||
|
|
5fb9a007f9 | ||
|
|
780a5aa158 | ||
|
|
ab2ff4ed9e | ||
|
|
2dad7553e6 | ||
|
|
acfa2e418b | ||
|
|
74101fda7a | ||
|
|
2ec1eec91c | ||
|
|
b3e966184a | ||
|
|
b09960858c | ||
|
|
84634af0c2 | ||
|
|
d60d7eaf2e | ||
|
|
6d6a994997 | ||
|
|
ab564c3b32 | ||
|
|
55b836388d | ||
|
|
3f45384b74 | ||
|
|
6fe2997fb5 | ||
|
|
cdd857ca2d | ||
|
|
c9e513350a | ||
|
|
7b2cbd6d1d | ||
|
|
90959ba34f | ||
|
|
0f6b6a2f2b | ||
|
|
666d98d86e | ||
|
|
e244efb153 | ||
|
|
67d824e5dc | ||
|
|
daf29f760d | ||
|
|
e5939d170f | ||
|
|
3526c9eff7 | ||
|
|
973889c73a | ||
|
|
70cd5f5cb6 | ||
|
|
ba6139f265 | ||
|
|
645e4913ba | ||
|
|
46eba8d03a | ||
|
|
9d2a649aac | ||
|
|
bf53537f00 | ||
|
|
1047e18732 | ||
|
|
2981a17b10 | ||
|
|
041416abf1 | ||
|
|
c117b67f3b | ||
|
|
bc2ac45588 | ||
|
|
95eba3fcfc | ||
|
|
e98b409f61 | ||
|
|
fedec47349 | ||
|
|
b63a565a37 | ||
|
|
67eb7fa380 | ||
|
|
a117886b71 | ||
|
|
152f6800da | ||
|
|
2f7e52f86f | ||
|
|
bb4b51c54f | ||
|
|
4c2303e4fa | ||
|
|
b70201db79 | ||
|
|
dd095147b0 | ||
|
|
08c4445e78 | ||
|
|
b892ad8185 | ||
|
|
a8ee1e43bf | ||
|
|
dd84674b98 | ||
|
|
ccec2cb45b | ||
|
|
f3a4500191 | ||
|
|
161a860f74 | ||
|
|
f58e8a24b0 | ||
|
|
9bc6a6dd74 | ||
|
|
c50364807d | ||
|
|
b3e3ecfc3a | ||
|
|
637b5fe785 | ||
|
|
6eed7b9002 | ||
|
|
899becb448 | ||
|
|
7e982ca877 | ||
|
|
26156253c7 | ||
|
|
9445d46bbf | ||
|
|
55bfcc853e | ||
|
|
4eeb1b25de | ||
|
|
42959196c6 | ||
|
|
d264b47b09 | ||
|
|
4e412d1808 | ||
|
|
73527da1f1 | ||
|
|
7986f198af | ||
|
|
adb1411313 | ||
|
|
2f24e9828e | ||
|
|
d7e5921e24 | ||
|
|
e3e6a1aaa5 | ||
|
|
0d1969fadd | ||
|
|
028e599d51 | ||
|
|
fa0dd5c23f | ||
|
|
7be5df8e70 | ||
|
|
adddf0e4c5 | ||
|
|
28cc13d583 | ||
|
|
c85b27b9b2 | ||
|
|
46795adf54 | ||
|
|
c8ab1735a5 | ||
|
|
4c759b40c8 | ||
|
|
1bab099f38 | ||
|
|
f95f814a8c | ||
|
|
ffcb0d669a | ||
|
|
81ca81d578 | ||
|
|
803f6d42f8 | ||
|
|
5366f16bcb | ||
|
|
ab18e49131 | ||
|
|
3c874244a7 | ||
|
|
d463b09c81 | ||
|
|
40301f260b | ||
|
|
72dbde1906 | ||
|
|
d68157faa8 | ||
|
|
c88991c028 | ||
|
|
262abe8520 | ||
|
|
9dd942df1a | ||
|
|
9ec552e08d | ||
|
|
2420164a9d | ||
|
|
822f2f6a4e | ||
|
|
d423a987ae | ||
|
|
70cefd817e | ||
|
|
2606af24a1 | ||
|
|
5b781cbda1 | ||
|
|
1519b52a74 | ||
|
|
4b73f6e0a7 | ||
|
|
88bcb14a4f | ||
|
|
5bd2cf5d50 | ||
|
|
e8fcfc67a8 | ||
|
|
580fc8c777 | ||
|
|
60fdcdd0e4 | ||
|
|
09fdff49a1 | ||
|
|
2f1b5034c4 | ||
|
|
6571ec53bb | ||
|
|
4a106f1bdf | ||
|
|
92c20c90ac | ||
|
|
31a920c5b3 | ||
|
|
a3b51c6803 | ||
|
|
2482c06e04 | ||
|
|
bbafa027e8 | ||
|
|
3ca62486d2 | ||
|
|
6371d60b70 | ||
|
|
8014e0281f | ||
|
|
253c11c855 | ||
|
|
49c5c73281 | ||
|
|
684edc1a28 | ||
|
|
cc60007679 | ||
|
|
6788180ca7 | ||
|
|
57c9b2544b | ||
|
|
c91995644c | ||
|
|
41fa1ad524 | ||
|
|
af14f03932 | ||
|
|
5a0b4fc351 | ||
|
|
f42a2bbeef | ||
|
|
8980e98a7d | ||
|
|
b401ae2772 | ||
|
|
e56fcdbdc4 | ||
|
|
4d7f6906dd | ||
|
|
f4b5977ad9 | ||
|
|
98c84e1556 | ||
|
|
8a24c81c03 | ||
|
|
41b9b31a01 | ||
|
|
9a781166b0 | ||
|
|
c03235ff83 | ||
|
|
be7c07a078 | ||
|
|
ee69ff9261 | ||
|
|
bcf903cd3f | ||
|
|
460e7f42d3 | ||
|
|
67d3adbdb0 | ||
|
|
d88ecbaa58 | ||
|
|
1a64ae4bd4 | ||
|
|
bfed6e3a87 | ||
|
|
b1d5e9e22d | ||
|
|
d35a7518fa | ||
|
|
d802ee80ee | ||
|
|
c74e0423f3 | ||
|
|
03e96d4af2 | ||
|
|
aac010c4a9 | ||
|
|
753dc6cc0c | ||
|
|
ab327b452d |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -7,6 +7,7 @@ coverage.html
|
||||
*.pid
|
||||
*.gz
|
||||
dump.rdb
|
||||
.DS_Store
|
||||
|
||||
pids
|
||||
logs
|
||||
@@ -15,6 +16,14 @@ 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
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
11
.npmignore
11
.npmignore
@@ -8,3 +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,5 +1,3 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- '0.10'
|
||||
install: npm install --registry=http://registry.cnpmjs.org
|
||||
script: make test-coveralls
|
||||
- '0.11'
|
||||
|
||||
8
AUTHORS
Normal file
8
AUTHORS
Normal file
@@ -0,0 +1,8 @@
|
||||
# Ordered by date of first contribution.
|
||||
# Auto-generated by 'contributors' on Mon, 03 Mar 2014 13:01:28 GMT.
|
||||
# https://github.com/xingrz/node-contributors
|
||||
|
||||
fengmk2 <fengmk2@gmail.com> (https://github.com/fengmk2)
|
||||
dead-horse <dead_horse@qq.com> (https://github.com/dead-horse)
|
||||
alsotang <alsotang@gmail.com> (https://github.com/alsotang)
|
||||
4simple <wondger@qq.com> (https://github.com/4simple)
|
||||
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)
|
||||
520
History.md
520
History.md
@@ -1,70 +1,532 @@
|
||||
|
||||
0.1.2 / 2013-12-19
|
||||
0.8.0 / 2014-07-21
|
||||
==================
|
||||
|
||||
* fix times not exists canot sync bug. fixed #101
|
||||
* 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](https://github.com/cnpm/cnpmjs.org/issues/1) bug
|
||||
|
||||
0.3.1 / 2014-02-27
|
||||
==================
|
||||
|
||||
* add etag fixed [#224](https://github.com/cnpm/cnpmjs.org/issues/224)
|
||||
* travis ci install on source npm
|
||||
|
||||
0.3.0 / 2014-02-27
|
||||
==================
|
||||
|
||||
* fix typo and dont sync not exists pkgs
|
||||
* use koa-middlewares
|
||||
* fix signed cookie not work on npm@1.3.25; node --harmony-generators
|
||||
* fix opensearch test case
|
||||
* update koa bodyparser
|
||||
* logger.error(err) should send err stack email notice
|
||||
* json body parse limit and bug fix.
|
||||
* fix sync 404 reason not clear
|
||||
* all controllers to koa
|
||||
* controller/web/user.js to koa
|
||||
* change web connect to koa
|
||||
* use outputError
|
||||
* use yield exports.addPackageAndDist.call(this, next);
|
||||
* add end() when ws write end
|
||||
* fix yield coWrite
|
||||
* fix all the test of registry module.test.js
|
||||
* convert registry/module.js to koa type
|
||||
* fix auth middleware
|
||||
* finish registry user controller koa and update mm to support thunkify. fixed [#196](https://github.com/cnpm/cnpmjs.org/issues/196)
|
||||
* change controllers/user.js to koa
|
||||
* thunkify all proxy
|
||||
* convert all middlewares to koa type
|
||||
* change regsitry sync to koa
|
||||
* addd koa-jsonp, koa-bodyparser, fix / controller
|
||||
* first koa run registry home page /
|
||||
* Merge pull request [#212](https://github.com/cnpm/cnpmjs.org/issues/212) from cnpm/fix-sync-404
|
||||
* return friendly 404 reason
|
||||
* Merge pull request [#211](https://github.com/cnpm/cnpmjs.org/issues/211) from cnpm/bug-fix
|
||||
* override json limit to default 10mb. fixed [#209](https://github.com/cnpm/cnpmjs.org/issues/209)
|
||||
* fix [#210](https://github.com/cnpm/cnpmjs.org/issues/210) addPackageAndDist package version detect bug
|
||||
|
||||
0.2.27 / 2014-02-19
|
||||
==================
|
||||
|
||||
* support json result in search, fixed [#189](https://github.com/cnpm/cnpmjs.org/issues/189)
|
||||
|
||||
0.2.26 / 2014-02-19
|
||||
==================
|
||||
|
||||
* npm publish also need to add deps
|
||||
|
||||
0.2.25 / 2014-02-19
|
||||
==================
|
||||
|
||||
* max handle number of package.json `dependencies` property
|
||||
* Dependents support. fixed [#190](https://github.com/cnpm/cnpmjs.org/issues/190)
|
||||
|
||||
0.2.24 / 2014-02-13
|
||||
==================
|
||||
|
||||
* fix if delete all the versions
|
||||
* refactor remove module, fixed [#186](https://github.com/cnpm/cnpmjs.org/issues/186)
|
||||
|
||||
0.2.23 / 2014-01-26
|
||||
==================
|
||||
|
||||
* system admin can add, publish, remove the packages. fixed [#176](https://github.com/cnpm/cnpmjs.org/issues/176)
|
||||
|
||||
0.2.22 / 2014-01-26
|
||||
==================
|
||||
|
||||
* add keyword and search support keyword. [#181](https://github.com/cnpm/cnpmjs.org/issues/181)
|
||||
|
||||
0.2.21 / 2014-01-24
|
||||
==================
|
||||
|
||||
* refactor code styles on package.html
|
||||
* nav-tabs e.preventDefault
|
||||
* 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
|
||||
==================
|
||||
|
||||
* hotfix sync missing dependencies and readmes
|
||||
* fix sync readme error, fixed [#174](https://github.com/cnpm/cnpmjs.org/issues/174)
|
||||
* add updateReadme in module
|
||||
|
||||
0.2.19 / 2014-01-22
|
||||
==================
|
||||
|
||||
* npm install no need to check authorization header. fixed [#171](https://github.com/cnpm/cnpmjs.org/issues/171)
|
||||
|
||||
0.2.18 / 2014-01-20
|
||||
==================
|
||||
|
||||
* 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
|
||||
==================
|
||||
|
||||
* custom logo url
|
||||
* hotfix layout bug
|
||||
|
||||
0.2.16 / 2014-01-16
|
||||
==================
|
||||
|
||||
* fix publish-time bug
|
||||
|
||||
0.2.15 / 2014-01-16
|
||||
==================
|
||||
|
||||
* add publish_time to debug
|
||||
|
||||
0.2.14 / 2014-01-16
|
||||
==================
|
||||
|
||||
* add make autod
|
||||
* update publish_time, fixed [#163](https://github.com/cnpm/cnpmjs.org/issues/163)
|
||||
|
||||
0.2.13 / 2014-01-15
|
||||
==================
|
||||
|
||||
* markdown tmpl not support footer, need to wrap on app start
|
||||
|
||||
0.2.12 / 2014-01-15
|
||||
==================
|
||||
|
||||
* add footer and npm client name customable
|
||||
|
||||
0.2.11 / 2014-01-15
|
||||
==================
|
||||
|
||||
* package page contributor link to search, default is true
|
||||
|
||||
0.2.10 / 2014-01-14
|
||||
==================
|
||||
|
||||
* fix [#155](https://github.com/cnpm/cnpmjs.org/issues/155) Content-Disposition wrong.
|
||||
|
||||
0.2.9 / 2014-01-14
|
||||
==================
|
||||
|
||||
* support startkey=c and startkey="c"
|
||||
* 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
|
||||
==================
|
||||
|
||||
* dont show err stack on test env
|
||||
* add download link for package page
|
||||
|
||||
0.2.7 / 2014-01-13
|
||||
==================
|
||||
|
||||
* add shasum when nfs.upload and hfs.uploadBuffer, fixed [#148](https://github.com/cnpm/cnpmjs.org/issues/148)
|
||||
|
||||
0.2.6 / 2014-01-13
|
||||
==================
|
||||
|
||||
* support custom session store, fixed [#146](https://github.com/cnpm/cnpmjs.org/issues/146)
|
||||
|
||||
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
|
||||
==================
|
||||
|
||||
* set main script to index.js, fixed [#142](https://github.com/cnpm/cnpmjs.org/issues/142)
|
||||
|
||||
0.2.3 / 2014-01-10
|
||||
==================
|
||||
|
||||
* Dont show sync button on private package
|
||||
* Sync package as publish with no deps. fixed [#138](https://github.com/cnpm/cnpmjs.org/issues/138)
|
||||
|
||||
0.2.2 / 2014-01-10
|
||||
==================
|
||||
|
||||
* keep compatibility
|
||||
* qnfs upload only callback a url
|
||||
* compat remove package
|
||||
* set tarball url
|
||||
* 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](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](https://github.com/cnpm/cnpmjs.org/issues/119) hasOwnProperty check admin bug.
|
||||
|
||||
0.2.0 / 2013-12-27
|
||||
==================
|
||||
|
||||
* remove to lower case
|
||||
* 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](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
|
||||
|
||||
0.1.3 / 2013-12-20
|
||||
==================
|
||||
|
||||
* add favicon
|
||||
* Fix auth middleware bug (@alsotang)
|
||||
* make sure all packages name are lower case
|
||||
* select ids from tag
|
||||
* fix nodejsctl
|
||||
* 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](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](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
|
||||
@@ -73,15 +535,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
|
||||
@@ -92,9 +554,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
|
||||
|
||||
54
Makefile
54
Makefile
@@ -1,30 +1,54 @@
|
||||
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
|
||||
@npm install $(REGISTRY) \
|
||||
--disturl=https://npm.taobao.org/dist
|
||||
|
||||
test: install
|
||||
@NODE_ENV=test ./node_modules/mocha/bin/mocha \
|
||||
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
|
||||
contributors: install
|
||||
@./node_modules/.bin/contributors -f plain -o AUTHORS
|
||||
|
||||
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
|
||||
autod: install
|
||||
@./node_modules/.bin/autod -w -e public,view,docs,backup,coverage
|
||||
@$(MAKE) install
|
||||
|
||||
.PHONY: test
|
||||
|
||||
99
README.md
99
README.md
@@ -1,45 +1,112 @@
|
||||
cnpmjs.org
|
||||
=======
|
||||
|
||||
[](http://travis-ci.org/fengmk2/cnpmjs.org) [](https://coveralls.io/r/fengmk2/cnpmjs.org)[](https://gemnasium.com/fengmk2/cnpmjs.org)
|
||||
[](http://travis-ci.org/cnpm/cnpmjs.org) [](https://gemnasium.com/cnpm/cnpmjs.org)
|
||||
|
||||
[](https://nodei.co/npm/cnpmjs.org/)
|
||||
[](https://nodei.co/npm/cnpmjs.org/)
|
||||
|
||||

|
||||

|
||||
|
||||
## What is this?
|
||||
|
||||
Private npm registry and web for Enterprise, base on MySQL and Simple File Store.
|
||||
Private npm registry and web for Enterprise, base on [koa](http://koajs.com/),
|
||||
MySQL and [Simple Store Service](https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide).
|
||||
|
||||
@[JacksonTian](https://github.com/JacksonTian/) had a talk about [private npm](https://speakerdeck.com/jacksontian/qi-ye-ji-node-dot-jskai-fa).
|
||||
Our goal is to provide a low cost maintenance 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)
|
||||
|
||||

|
||||
|
||||
## Install
|
||||
## Develop on your local machine
|
||||
|
||||
### 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
|
||||
$ nohup mysqld &
|
||||
```
|
||||
|
||||
## Usage
|
||||
### Clone codes and run test
|
||||
|
||||
```js
|
||||
$ node 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
|
||||
```
|
||||
|
||||
## How to contribute
|
||||
|
||||
* Clone the project
|
||||
* Checkout a new branch
|
||||
* Add new features or fix bugs in the new branch
|
||||
* Make a pull request and we will review it ASAP
|
||||
|
||||
Tips: make sure your code is following the [node-style-guide](https://github.com/felixge/node-style-guide).
|
||||
|
||||
## Authors
|
||||
|
||||
```bash
|
||||
$ git summary
|
||||
|
||||
project : cnpmjs.org
|
||||
repo age : 2 weeks
|
||||
active : 73 days
|
||||
commits : 186
|
||||
files : 83
|
||||
repo age : 4 months ago
|
||||
commits : 472
|
||||
active : 167 days
|
||||
files : 104
|
||||
authors :
|
||||
109 fengmk2 58.6%
|
||||
77 dead_horse 41.4%
|
||||
272 fengmk2 57.6%
|
||||
195 dead_horse 41.3%
|
||||
2 4simple 0.4%
|
||||
2 Stanley Zheng 0.4%
|
||||
1 Alsotang 0.2%
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
@@ -7,10 +7,11 @@ export NODE_ENV='production'
|
||||
ulimit -c unlimited
|
||||
|
||||
cd `dirname $0`/..
|
||||
NODEJS=node
|
||||
NODEJS='node --harmony'
|
||||
BASE_HOME=`pwd`
|
||||
PROJECT_NAME=`basename ${BASE_HOME}`
|
||||
STDOUT_LOG=`$NODEJS -e "console.log(require('path').join(require('$BASE_HOME/config').logdir, 'nodejs_stdout.log'))"`
|
||||
STDOUT_LOG=`$NODEJS -e "console.log(require('path').join(require('$BASE_HOME/config').logdir, 'nodejs_stdout.log'));\
|
||||
process.exit(0);"`
|
||||
NODE_DISPATH_PATH=${BASE_HOME}/dispatch.js
|
||||
PROG_NAME=$0
|
||||
ACTION=$1
|
||||
@@ -47,9 +48,9 @@ stop()
|
||||
{
|
||||
get_pid
|
||||
if [ ! -z "$PID" ]; then
|
||||
echo "Waiting $PROJECT_NAME stop for 5s ..."
|
||||
echo "Waiting $PROJECT_NAME stop for 2s ..."
|
||||
kill -15 $PID
|
||||
sleep 5
|
||||
sleep 2
|
||||
|
||||
node_num=`ps -ef |grep node|grep ${PROJECT_NAME}|grep -v grep|wc -l`
|
||||
if [ $node_num != 0 ]; then
|
||||
|
||||
74
bin/restore_module_deps.js
Normal file
74
bin/restore_module_deps.js
Normal file
@@ -0,0 +1,74 @@
|
||||
/**!
|
||||
* cnpmjs.org - bin/restore_module_deps.js
|
||||
*
|
||||
* Copyright(c) 2014
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var mysql = require('../common/mysql');
|
||||
var Module = require('../proxy/module');
|
||||
var ModuleDeps = require('../proxy/module_deps');
|
||||
|
||||
var addCount = 0;
|
||||
|
||||
function restore(id, callback) {
|
||||
var sql = 'SELECT id, name, package FROM module WHERE id > ? ORDER BY id ASC LIMIT 1000';
|
||||
mysql.query(sql, [id], function (err, rows) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (rows.length === 0) {
|
||||
return callback(null, []);
|
||||
}
|
||||
|
||||
console.log('[%s] got %d rows', id, rows.length);
|
||||
|
||||
rows.forEach(function (r) {
|
||||
Module.parseRow(r);
|
||||
if (!r.package) {
|
||||
return;
|
||||
}
|
||||
var deps = Object.keys(r.package.dependencies || {});
|
||||
if (!Array.isArray(deps) || !deps.length) {
|
||||
return;
|
||||
}
|
||||
deps.forEach(function (dep) {
|
||||
ModuleDeps.add(dep, r.name, function (err) {
|
||||
// console.log('[%s] add %s <= %s, error: %s', id, dep, r.name, err);
|
||||
});
|
||||
});
|
||||
addCount += deps.length;
|
||||
});
|
||||
setTimeout(function () {
|
||||
console.log('[%s] add %d relations', id, addCount);
|
||||
callback(null, rows);
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
var id = 0;
|
||||
function run() {
|
||||
restore(id, function (err, rows) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
if (rows.length === 0) {
|
||||
console.log('finished, last id: %s, exit.', id);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
id = rows[rows.length - 1].id;
|
||||
run();
|
||||
});
|
||||
}
|
||||
|
||||
run();
|
||||
102
common/logger.js
102
common/logger.js
@@ -1,7 +1,12 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - common/logger.js
|
||||
* Copyright(c) 2013
|
||||
* Author: dead_horse <undefined>
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -10,61 +15,48 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var 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 moment = require('moment');
|
||||
var logstream = require('logfilestream');
|
||||
var ms = require('ms');
|
||||
|
||||
var isTEST = process.env.NODE_ENV === 'test';
|
||||
var ONE_DAY = ms('1d');
|
||||
var levels = ['info', 'warn', 'error'];
|
||||
var categories = ['sync_info', 'sync_error'];
|
||||
|
||||
levels.forEach(function (catetory) {
|
||||
var options = {
|
||||
logdir: config.logdir,
|
||||
duration: ONE_DAY,
|
||||
nameformat: '[' + catetory + '.]YYYY-MM-DD[.log]'
|
||||
};
|
||||
var stream = logstream(options);
|
||||
function write(msg) {
|
||||
var time = moment().format('YYYY-MM-DD HH:mm:ss.SSS');
|
||||
if (msg instanceof Error) {
|
||||
var err = {
|
||||
name: msg.name,
|
||||
code: msg.code,
|
||||
message: msg.message,
|
||||
stack: msg.stack,
|
||||
host: msg.host,
|
||||
url: msg.url,
|
||||
data: msg.data
|
||||
};
|
||||
if (err.name === 'Error' && typeof err.code === 'string') {
|
||||
err.name = err.code + err.name;
|
||||
}
|
||||
err.name += 'Exception';
|
||||
if (err.host) {
|
||||
err.message += ' (' + err.host + ')';
|
||||
}
|
||||
msg = util.format('%s nodejs.%s: %s\nURL: %s\nData: %j\n%s\n\n',
|
||||
time,
|
||||
err.name,
|
||||
err.stack,
|
||||
err.url,
|
||||
err.data,
|
||||
time
|
||||
);
|
||||
} else {
|
||||
msg = time + ' ' + util.format.apply(util, arguments) + '\n';
|
||||
}
|
||||
if (!isTEST) {
|
||||
if (config.debug) {
|
||||
var level = catetory;
|
||||
console.log('[' + level + '] ' + msg);
|
||||
} else {
|
||||
stream.write(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports[catetory] = write;
|
||||
var logger = module.exports = Logger({
|
||||
categories: categories,
|
||||
dir: config.logdir,
|
||||
duration: '1d',
|
||||
format: '[{category}.]YYYY-MM-DD[.log]',
|
||||
stdout: config.debug && !isTEST,
|
||||
errorFormater: errorFormater
|
||||
});
|
||||
|
||||
var to = [];
|
||||
for (var name in config.admins) {
|
||||
to.push(config.admins[name]);
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - common/mail.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var ready = require('ready');
|
||||
var mysql = require('mysql');
|
||||
var config = require('../config');
|
||||
@@ -35,7 +36,13 @@ var pool = mysql.createPool({
|
||||
exports.pool = pool;
|
||||
|
||||
exports.query = function (sql, values, cb) {
|
||||
pool.query(sql, values, cb);
|
||||
if (typeof values === 'function') {
|
||||
cb = values;
|
||||
values = null;
|
||||
}
|
||||
pool.query(sql, values, function (err, rows) {
|
||||
cb(err, rows);
|
||||
});
|
||||
};
|
||||
|
||||
exports.queryOne = function (sql, values, cb) {
|
||||
@@ -57,6 +64,8 @@ exports.escape = function (val) {
|
||||
|
||||
ready(exports);
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
function init() {
|
||||
exports.query('show tables', function (err, rows) {
|
||||
if (err) {
|
||||
|
||||
@@ -14,11 +14,16 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Upload file
|
||||
*
|
||||
@@ -26,20 +31,53 @@ var client = qn.create(config.qn);
|
||||
* @param {Object} options
|
||||
* - {String} key
|
||||
* - {Number} size
|
||||
* @param {Function(err, result)} callback
|
||||
* - {Object} result
|
||||
* - {String} url
|
||||
*/
|
||||
exports.upload = function (filepath, options, callback) {
|
||||
client.delete(options.key, function (err, data) {
|
||||
client.uploadFile(filepath, {key: options.key, size: options.size}, callback);
|
||||
exports.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) {
|
||||
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
|
||||
});
|
||||
};
|
||||
|
||||
exports.remove = function *(key) {
|
||||
try {
|
||||
return yield client.delete(key);
|
||||
} catch (err) {
|
||||
if (err.name === 'QiniuFileNotExistsError') {
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
38
common/redis.js
Normal file
38
common/redis.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/**!
|
||||
* 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);
|
||||
|
||||
_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;
|
||||
}
|
||||
@@ -15,27 +15,20 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var connect = require('connect');
|
||||
var RedisStore = require('connect-redis')(connect);
|
||||
var middlewares = require('koa-middlewares');
|
||||
var config = require('../config');
|
||||
|
||||
var session;
|
||||
var key = 'AuthSession';
|
||||
var cookie = { path: '/', httpOnly: true, maxAge: 3600000 * 24 * 30 };
|
||||
var cookie = { path: '/', httpOnly: true, maxAge: 3600000 * 24 * 365, signed: false };
|
||||
var options = {
|
||||
key: key,
|
||||
cookie: cookie,
|
||||
defer: true,
|
||||
rolling: false
|
||||
};
|
||||
|
||||
if (config.debug) {
|
||||
session = connect.cookieSession({
|
||||
secret: config.sessionSecret,
|
||||
key: key,
|
||||
cookie: cookie
|
||||
});
|
||||
} else {
|
||||
session = connect.session({
|
||||
key: key,
|
||||
secret: config.sessionSecret,
|
||||
store: new RedisStore(config.redis),
|
||||
cookie: cookie,
|
||||
});
|
||||
if (!config.debug) {
|
||||
options.store = config.sessionStore || middlewares.RedisStore(config.redis);
|
||||
}
|
||||
|
||||
module.exports = session;
|
||||
module.exports = middlewares.session(options);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - config/index.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -17,47 +17,56 @@
|
||||
|
||||
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,
|
||||
registryPort: 7001,
|
||||
webPort: 7002,
|
||||
bindingHost: '127.0.0.1', // only binding on 127.0.0.1 for local access
|
||||
enableCluster: false,
|
||||
numCPUs: os.cpus().length,
|
||||
debug: true, // if debug
|
||||
logdir: path.join(root, '.tmp', 'logs'),
|
||||
viewCache: false,
|
||||
// mysql config
|
||||
mysqlServers: [
|
||||
{
|
||||
host: 'keydiary.mysql.rds.aliyuncs.com', // 'db4free.net'
|
||||
host: '127.0.0.1',
|
||||
port: 3306,
|
||||
user: 'cnpmjs',
|
||||
password: 'cnpmjs123'
|
||||
user: 'root',
|
||||
password: ''
|
||||
}
|
||||
],
|
||||
mysqlDatabase: 'cnpmjstest',
|
||||
mysqlDatabase: 'cnpmjs_test',
|
||||
mysqlMaxConnections: 4,
|
||||
mysqlQueryTimeout: 5000,
|
||||
|
||||
sessionSecret: 'cnpmjs.org test session secret',
|
||||
redis: {
|
||||
host: 'pub-redis-19533.us-east-1-4.3.ec2.garantiadata.com',
|
||||
port: 19533,
|
||||
pass: 'cnpmjs_dev'
|
||||
// host: 'pub-redis-19533.us-east-1-4.3.ec2.garantiadata.com',
|
||||
// port: 19533,
|
||||
// pass: 'cnpmjs_dev'
|
||||
},
|
||||
uploadDir: path.join(root, 'public', 'dist'),
|
||||
jsonLimit: '10mb', // max request json body size
|
||||
uploadDir: path.join(root, '.dist'),
|
||||
// qiniu cdn: http://www.qiniu.com/, it free for dev.
|
||||
qn: {
|
||||
accessKey: "iN7NgwM31j4-BZacMjPrOQBs34UG1maYCAQmhdCV",
|
||||
secretKey: "6QTOr2Jg1gcZEWDQXKOGZh5PziC2MCV5KsntT70j",
|
||||
bucket: "qtestbucket",
|
||||
domain: "http://qtestbucket.qiniudn.com"
|
||||
// accessKey: "iN7NgwM31j4-BZacMjPrOQBs34UG1maYCAQmhdCV",
|
||||
// secretKey: "6QTOr2Jg1gcZEWDQXKOGZh5PziC2MCV5KsntT70j",
|
||||
// bucket: "qtestbucket",
|
||||
// domain: "http://qtestbucket.qiniudn.com",
|
||||
accessKey: "5UyUq-l6jsWqZMU6tuQ85Msehrs3Dr58G-mCZ9rE",
|
||||
secretKey: "YaRsPKiYm4nGUt8mdz2QxeV5Q_yaUzVxagRuWTfM",
|
||||
bucket: "qiniu-sdk-test",
|
||||
domain: "http://qiniu-sdk-test.qiniudn.com",
|
||||
},
|
||||
|
||||
mail: {
|
||||
@@ -68,14 +77,25 @@ var config = {
|
||||
user: 'address@gmail.com',
|
||||
pass: 'your password',
|
||||
ssl: true,
|
||||
debug: true
|
||||
debug: false
|
||||
},
|
||||
|
||||
noticeSyncDistError: true,
|
||||
disturl: 'http://nodejs.org/dist',
|
||||
syncDist: false,
|
||||
logoURL: '//ww4.sinaimg.cn/large/69c1d4acgw1ebfly5kjlij208202oglr.jpg',
|
||||
registryHost: 'r.cnpmjs.org',
|
||||
// customReadmeFile: __dirname + '/web_readme.md',
|
||||
customReadmeFile: '', // you can use your custom readme file instead the cnpm one
|
||||
customFooter: '', // you can add copyright and site total script html here
|
||||
npmClientName: 'cnpm', // use `${name} install package`
|
||||
packagePageContributorSearch: true, // package page contributor link to search, default is true
|
||||
sourceNpmRegistry: 'http://registry.npmjs.org',
|
||||
enablePrivate: true, // enable private mode, only admin can publish, other use just can sync package from source npm
|
||||
admins: {
|
||||
admin: 'admin@cnpmjs.org',
|
||||
// name: email
|
||||
fengmk2: 'fengmk2@gmail.com',
|
||||
admin: 'admin@cnpmjs.org',
|
||||
dead_horse: 'dead_horse@qq.com',
|
||||
cnpmjstest10: 'cnpmjstest10@cnpmjs.org',
|
||||
},
|
||||
@@ -83,18 +103,35 @@ var config = {
|
||||
backupFilePrefix: '/cnpm/backup/', // backup filepath prefix
|
||||
syncModel: 'none', // 'none', 'all', 'exist'
|
||||
syncConcurrency: 1,
|
||||
syncInterval: '10m', // sync interval, default is 10 minutes
|
||||
maxDependencies: 200, // max handle number of package.json `dependencies` property
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
// 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);
|
||||
mkdirp.sync(config.uploadDir);
|
||||
|
||||
module.exports = config;
|
||||
|
||||
config.loadConfig = function (customConfig) {
|
||||
if (!customConfig) {
|
||||
return;
|
||||
}
|
||||
copy(customConfig).override(config);
|
||||
};
|
||||
|
||||
@@ -14,10 +14,15 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var moment = require('moment');
|
||||
var DownloadTotal = require('../proxy/download');
|
||||
|
||||
exports.total = function (name, callback) {
|
||||
if (typeof name === 'function') {
|
||||
callback = name;
|
||||
name = null;
|
||||
}
|
||||
var end = moment();
|
||||
var start = end.clone().subtract('months', 1).startOf('month');
|
||||
var lastday = end.clone().subtract('days', 1).format('YYYY-MM-DD');
|
||||
@@ -74,3 +79,5 @@ exports.total = function (name, callback) {
|
||||
|
||||
DownloadTotal[method].apply(DownloadTotal, args);
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - controllers/registry/user.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -15,148 +15,199 @@
|
||||
* 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 User = require('../../proxy/user');
|
||||
var eventproxy = require('eventproxy');
|
||||
var config = require('../../config');
|
||||
|
||||
exports.show = function (req, res, next) {
|
||||
var name = req.params.name;
|
||||
User.get(name, function (err, row) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (!row) {
|
||||
return next();
|
||||
}
|
||||
exports.show = function *(next) {
|
||||
var name = this.params.name;
|
||||
var user = yield User.get(name);
|
||||
if (!user) {
|
||||
return yield *next;
|
||||
}
|
||||
|
||||
res.setHeader('etag', '"' + row.rev + '"');
|
||||
var data = {
|
||||
_id: 'org.couchdb.user:' + row.name,
|
||||
_rev: row.rev,
|
||||
name: row.name,
|
||||
email: row.email,
|
||||
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: row.gmt_modified,
|
||||
date: user.gmt_modified,
|
||||
};
|
||||
res.json(data);
|
||||
});
|
||||
}
|
||||
data._cnpm_meta = {
|
||||
id: user.id,
|
||||
npm_user: user.npm_user,
|
||||
gmt_create: user.gmt_create,
|
||||
gmt_modified: user.gmt_modified,
|
||||
admin: !!config.admins[user.name],
|
||||
};
|
||||
|
||||
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 (req, res, next) {
|
||||
var name = req.params.name;
|
||||
var body = req.body || {};
|
||||
// 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: '18d8d51936478446a5466d4fb1633b80f3838b4caaa03649a885ac722cd6',
|
||||
// password_sha: '8f4408912a6db1d96b132a90856d99db029cef3d',
|
||||
// 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,
|
||||
email: body.email,
|
||||
ip: req.socket && req.socket.remoteAddress || '0.0.0.0',
|
||||
ip: this.ip || '0.0.0.0',
|
||||
// roles: body.roles || [],
|
||||
};
|
||||
|
||||
ensurePasswordSalt(user, body);
|
||||
|
||||
if (!user.name || !user.salt || !user.password_sha || !user.email) {
|
||||
return res.json(422, {
|
||||
this.status = 422;
|
||||
this.body = {
|
||||
error: 'paramError',
|
||||
reason: 'params missing'
|
||||
});
|
||||
reason: 'params missing, name, email or password missing.'
|
||||
};
|
||||
return;
|
||||
}
|
||||
debug('add user: %j', user);
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(next);
|
||||
|
||||
User.get(name, ep.doneLater(function (row) {
|
||||
if (row) {
|
||||
return res.json(409, {
|
||||
error: 'conflict',
|
||||
reason: 'Document update conflict.'
|
||||
});
|
||||
}
|
||||
User.add(user, ep.done('add'));
|
||||
}));
|
||||
var existUser = yield User.get(name);
|
||||
if (existUser) {
|
||||
this.status = 409;
|
||||
this.body = {
|
||||
error: 'conflict',
|
||||
reason: 'User ' + name + ' already exists.'
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
ep.once('add', function (result) {
|
||||
res.setHeader('etag', '"' + result.rev + '"');
|
||||
// location: 'http://registry.npmjs.org/_users/org.couchdb.user:cnpmjstest1',
|
||||
res.json(201, {
|
||||
ok: true,
|
||||
id: 'org.couchdb.user:' + name,
|
||||
rev: result.rev
|
||||
});
|
||||
});
|
||||
var result = yield User.add(user);
|
||||
this.etag = '"' + result.rev + '"';
|
||||
this.status = 201;
|
||||
this.body = {
|
||||
ok: true,
|
||||
id: 'org.couchdb.user:' + name,
|
||||
rev: result.rev
|
||||
};
|
||||
};
|
||||
|
||||
exports.authSession = function (req, res, next) {
|
||||
exports.authSession = function *() {
|
||||
// body: {"name":"foo","password":"****"}
|
||||
var body = req.body || {};
|
||||
var body = this.request.body || {};
|
||||
var name = body.name;
|
||||
var password = body.password;
|
||||
User.auth(name, password, function (err, user) {
|
||||
debug('authSession %s: %j', name, user);
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (!user) {
|
||||
return res.json(401, {ok: false, name: null, roles: []});
|
||||
}
|
||||
var user = yield User.auth(name, password);
|
||||
debug('authSession %s: %j', name, user);
|
||||
|
||||
req.session.name = user.name;
|
||||
res.json(200, {ok: true, name: user.name, roles: []});
|
||||
});
|
||||
if (!user) {
|
||||
this.status = 401;
|
||||
this.body = {ok: false, name: null, roles: []};
|
||||
return;
|
||||
}
|
||||
var session = yield *this.session;
|
||||
session.name = user.name;
|
||||
this.body = {ok: true, name: user.name, roles: []};
|
||||
};
|
||||
|
||||
exports.update = function (req, res, next) {
|
||||
var name = req.params.name;
|
||||
var rev = req.params.rev;
|
||||
exports.update = function *(next) {
|
||||
var name = this.params.name;
|
||||
var rev = this.params.rev;
|
||||
if (!name || !rev) {
|
||||
return 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, req.session.name);
|
||||
|
||||
if (name !== req.session.name) {
|
||||
// must authSession first
|
||||
res.statusCode = 401;
|
||||
return res.json({
|
||||
if (name !== this.user.name) {
|
||||
// must auth user first
|
||||
this.status = 401;
|
||||
this.body = {
|
||||
error: 'unauthorized',
|
||||
reason: 'Name is incorrect.'
|
||||
});
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
var body = req.body || {};
|
||||
var body = this.request.body || {};
|
||||
var user = {
|
||||
name: body.name,
|
||||
salt: body.salt,
|
||||
password_sha: body.password_sha,
|
||||
email: body.email,
|
||||
ip: req.socket && req.socket.remoteAddress || '0.0.0.0',
|
||||
ip: this.ip || '0.0.0.0',
|
||||
rev: body.rev || body._rev,
|
||||
// roles: body.roles || [],
|
||||
};
|
||||
User.update(user, function (err, result) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
//check rev error
|
||||
if (!result) {
|
||||
return res.json(409, {
|
||||
error: 'conflict',
|
||||
reason: 'Document update conflict.'
|
||||
});
|
||||
}
|
||||
res.json(201, {
|
||||
ok: true,
|
||||
id: 'org.couchdb.user:' + user.name,
|
||||
rev: result.rev
|
||||
});
|
||||
});
|
||||
|
||||
ensurePasswordSalt(user, body);
|
||||
|
||||
if (!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;
|
||||
this.body = {
|
||||
error: 'conflict',
|
||||
reason: 'Document update conflict.'
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
this.status = 201;
|
||||
this.body = {
|
||||
ok: true,
|
||||
id: 'org.couchdb.user:' + user.name,
|
||||
rev: result.rev
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**!
|
||||
* cnpmjs.org - controllers/download.js
|
||||
* cnpmjs.org - controllers/sync.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
@@ -13,38 +13,67 @@
|
||||
/**
|
||||
* 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 (req, res, next) {
|
||||
var username = req.session.name || 'anonymous';
|
||||
var name = req.params.name;
|
||||
SyncModuleWorker.sync(name, username, function (err, result) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (!result.ok) {
|
||||
return res.json(result.statusCode, result.pkg);
|
||||
}
|
||||
res.json(201, {
|
||||
ok: true,
|
||||
logId: result.logId
|
||||
});
|
||||
});
|
||||
exports.sync = function* () {
|
||||
var username = this.user.name || 'anonymous';
|
||||
var name = this.params.name || this.params[0];
|
||||
var publish = this.query.publish === 'true';
|
||||
var noDep = this.query.nodeps === 'true';
|
||||
debug('sync %s with query: %j', name, this.query);
|
||||
if (publish && !this.user.isAdmin) {
|
||||
this.status = 403;
|
||||
this.body = {
|
||||
error: 'no_perms',
|
||||
reason: 'Only admin can publish'
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
var options = {
|
||||
publish: publish,
|
||||
noDep: noDep,
|
||||
};
|
||||
|
||||
var result = yield SyncModuleWorker.sync(name, username, options);
|
||||
debug('sync %s got %j', name, result);
|
||||
|
||||
// friendly 404 reason info
|
||||
if (result.statusCode === 404) {
|
||||
this.status = 404;
|
||||
this.body = {
|
||||
ok: false,
|
||||
reason: 'can not found ' + name + ' in the source registry'
|
||||
};
|
||||
return;
|
||||
}
|
||||
if (!result.ok) {
|
||||
this.status = result.statusCode || 500;
|
||||
this.body = result.pkg;
|
||||
return;
|
||||
}
|
||||
this.status = 201;
|
||||
this.body = {
|
||||
ok: true,
|
||||
logId: result.logId
|
||||
};
|
||||
};
|
||||
|
||||
exports.getSyncLog = function (req, res, next) {
|
||||
var logId = req.params.id;
|
||||
var name = req.params.name;
|
||||
var offset = Number(req.query.offset) || 0;
|
||||
Log.get(logId, function (err, row) {
|
||||
if (err || !row) {
|
||||
return next(err);
|
||||
}
|
||||
var log = row.log.trim();
|
||||
if (offset > 0) {
|
||||
log = log.split('\n').slice(offset).join('\n');
|
||||
}
|
||||
res.json(200, {ok: true, log: log});
|
||||
});
|
||||
exports.getSyncLog = function* (next) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
var log = row.log.trim();
|
||||
if (offset > 0) {
|
||||
log = log.split('\n').slice(offset).join('\n');
|
||||
}
|
||||
this.body = {ok: true, log: log};
|
||||
};
|
||||
|
||||
@@ -15,32 +15,25 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var microtime = require('microtime');
|
||||
var eventproxy = require('eventproxy');
|
||||
var Total = require('../proxy/total');
|
||||
var down = require('./download');
|
||||
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 (req, res, next) {
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(next);
|
||||
exports.show = function *() {
|
||||
var r = yield [Total.get(), Download.total()];
|
||||
var total = r[0];
|
||||
var download = r[1];
|
||||
|
||||
Total.get(ep.done('total'));
|
||||
down.total(null, ep.done('download'));
|
||||
ep.all('total', 'download', function (total, download) {
|
||||
total.download = download;
|
||||
total.db_name = 'registry';
|
||||
total.instance_start_time = startTime;
|
||||
total.node_version = process.version;
|
||||
total.app_version = version;
|
||||
total.donate = 'https://me.alipay.com/imk2';
|
||||
total.sync_model = config.syncModel;
|
||||
if (req.query.callback) {
|
||||
return res.jsonp(total, req.query.callback);
|
||||
}
|
||||
res.json(total);
|
||||
});
|
||||
total.download = download;
|
||||
total.db_name = 'registry';
|
||||
total.instance_start_time = startTime;
|
||||
total.node_version = process.version;
|
||||
total.app_version = version;
|
||||
total.donate = 'https://me.alipay.com/imk2';
|
||||
total.sync_model = config.syncModel;
|
||||
|
||||
this.body = 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,87 +14,192 @@
|
||||
* 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');
|
||||
var semver = require('semver');
|
||||
var marked = require('marked');
|
||||
var gravatar = require('gravatar');
|
||||
var humanize = require('humanize-number');
|
||||
var config = require('../../config');
|
||||
var Module = require('../../proxy/module');
|
||||
var down = require('../download');
|
||||
var sync = require('../sync');
|
||||
var Log = require('../../proxy/module_log');
|
||||
var ModuleDeps = require('../../proxy/module_deps');
|
||||
var setDownloadURL = require('../../lib/common').setDownloadURL;
|
||||
var ModuleStar = require('../../proxy/module_star');
|
||||
var packageService = require('../../services/package');
|
||||
|
||||
exports.display = function (req, res, next) {
|
||||
var params = req.params;
|
||||
var name = params.name;
|
||||
var tag = params.version;
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(next);
|
||||
exports.display = function* (next) {
|
||||
var params = this.params;
|
||||
// normal: {name: $name, version: $version}
|
||||
// scope: [$name, $version]
|
||||
var name = params.name || params[0];
|
||||
var tag = params.version || params[1];
|
||||
debug('display %s with %j', name, params);
|
||||
|
||||
if (tag) {
|
||||
var version = semver.valid(tag);
|
||||
if (version) {
|
||||
Module.get(name, version, ep.done('pkg'));
|
||||
} else {
|
||||
Module.getByTag(name, tag, ep.done('pkg'));
|
||||
}
|
||||
var getPackageMethod;
|
||||
var getPackageArgs;
|
||||
var version = semver.valid(tag || '');
|
||||
if (version) {
|
||||
getPackageMethod = 'get';
|
||||
getPackageArgs = [name, version];
|
||||
} else {
|
||||
Module.getByTag(name, 'latest', ep.done('pkg'));
|
||||
getPackageMethod = 'getByTag';
|
||||
getPackageArgs = [name, tag || 'latest'];
|
||||
}
|
||||
var r = yield [
|
||||
Module[getPackageMethod].apply(Module, getPackageArgs),
|
||||
down.total(name),
|
||||
ModuleDeps.list(name),
|
||||
ModuleStar.listUsers(name),
|
||||
packageService.listMaintainers(name)
|
||||
];
|
||||
var pkg = r[0];
|
||||
var download = r[1];
|
||||
var dependents = (r[2] || []).map(function (item) {
|
||||
return item.deps;
|
||||
});
|
||||
var users = r[3];
|
||||
var maintainers = r[4];
|
||||
|
||||
if (!pkg || !pkg.package) {
|
||||
return yield* next;
|
||||
}
|
||||
|
||||
down.total(name, ep.done('download'));
|
||||
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 || '';
|
||||
}
|
||||
|
||||
ep.all('pkg', 'download', function (pkg, download) {
|
||||
if (!pkg || !pkg.package) {
|
||||
return next();
|
||||
}
|
||||
if (maintainers.length > 0) {
|
||||
pkg.maintainers = maintainers;
|
||||
}
|
||||
|
||||
pkg.package.fromNow = moment(pkg.publish_time).fromNow();
|
||||
pkg = pkg.package;
|
||||
pkg.readme = marked(pkg.readme || '');
|
||||
if (pkg.maintainers) {
|
||||
for (var i = 0; i < pkg.maintainers.length; i++) {
|
||||
var maintainer = pkg.maintainers[i];
|
||||
if (maintainer.email) {
|
||||
maintainer.gravatar = gravatar.url(maintainer.email, {s: '50', d: 'retro'}, false);
|
||||
}
|
||||
if (pkg.maintainers) {
|
||||
for (var i = 0; i < pkg.maintainers.length; i++) {
|
||||
var maintainer = pkg.maintainers[i];
|
||||
if (maintainer.email) {
|
||||
maintainer.gravatar = gravatar.url(maintainer.email, {s: '50', d: 'retro'}, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setLicense(pkg);
|
||||
|
||||
for (var k in download) {
|
||||
download[k] = humanize(download[k]);
|
||||
if (pkg.contributors) {
|
||||
// registry.cnpmjs.org/compressible
|
||||
if (!Array.isArray(pkg.contributors)) {
|
||||
pkg.contributors = [pkg.contributors];
|
||||
}
|
||||
for (var i = 0; i < pkg.contributors.length; i++) {
|
||||
var contributor = pkg.contributors[i];
|
||||
if (contributor.email) {
|
||||
contributor.gravatar = gravatar.url(contributor.email, {s: '50', d: 'retro'}, true);
|
||||
}
|
||||
if (config.packagePageContributorSearch || !contributor.url) {
|
||||
contributor.url = '/~' + encodeURIComponent(contributor.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.render('package', {
|
||||
title: 'Package - ' + pkg.name,
|
||||
package: pkg,
|
||||
download: download
|
||||
});
|
||||
if (pkg.repository && pkg.repository.url) {
|
||||
pkg.repository.weburl = giturl.parse(pkg.repository.url) || pkg.repository.url;
|
||||
}
|
||||
|
||||
setLicense(pkg);
|
||||
|
||||
for (var k in download) {
|
||||
download[k] = humanize(download[k]);
|
||||
}
|
||||
setDownloadURL(pkg, this, config.registryHost);
|
||||
|
||||
pkg.dependents = dependents;
|
||||
|
||||
if (pkg.dist) {
|
||||
pkg.dist.size = bytes(pkg.dist.size || 0);
|
||||
}
|
||||
|
||||
yield this.render('package', {
|
||||
title: 'Package - ' + pkg.name,
|
||||
package: pkg,
|
||||
download: download
|
||||
});
|
||||
};
|
||||
|
||||
exports.search = function (req, res, next) {
|
||||
var params = req.params;
|
||||
var word = req.params.word;
|
||||
Module.search(word, function (err, packages) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
exports.search = function *(next) {
|
||||
var params = this.params;
|
||||
var word = params.word || params[0];
|
||||
debug('search %j', word);
|
||||
var result = yield Module.search(word);
|
||||
|
||||
res.render('search', {
|
||||
title: 'Keyword - ' + 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,
|
||||
packages: packages || []
|
||||
});
|
||||
match: match,
|
||||
packages: result.searchMatchs,
|
||||
keywords: result.keywordMatchs,
|
||||
};
|
||||
this.type = 'application/json; charset=utf-8';
|
||||
return;
|
||||
}
|
||||
yield this.render('search', {
|
||||
title: 'Keyword - ' + word,
|
||||
keyword: word,
|
||||
match: match,
|
||||
packages: result.searchMatchs,
|
||||
keywords: result.keywordMatchs,
|
||||
});
|
||||
};
|
||||
|
||||
exports.displaySync = function (req, res, next) {
|
||||
var name = req.params.name;
|
||||
res.render('sync', {
|
||||
exports.rangeSearch = function *(next) {
|
||||
var startKey = this.query.startkey || '';
|
||||
if (startKey[0] === '"') {
|
||||
startKey = startKey.substring(1);
|
||||
}
|
||||
if (startKey[startKey.length - 1] === '"') {
|
||||
startKey = startKey.substring(0, startKey.length - 1);
|
||||
}
|
||||
var limit = Number(this.query.limit) || 20;
|
||||
var result = yield Module.search(startKey, {limit: limit});
|
||||
|
||||
var packages = result.searchMatchs.concat(result.keywordMatchs);
|
||||
|
||||
var rows = [];
|
||||
for (var i = 0; i < packages.length; i++) {
|
||||
var p = packages[i];
|
||||
var row = {
|
||||
key: p.name,
|
||||
count: 1,
|
||||
value: {
|
||||
name: p.name,
|
||||
description: p.description,
|
||||
}
|
||||
};
|
||||
rows.push(row);
|
||||
}
|
||||
this.body = {
|
||||
rows: rows
|
||||
};
|
||||
};
|
||||
|
||||
exports.displaySync = function* (next) {
|
||||
var name = this.params.name || this.params[0] || this.query.name;
|
||||
yield this.render('sync', {
|
||||
name: name,
|
||||
title: 'Sync - ' + name
|
||||
});
|
||||
|
||||
@@ -15,31 +15,24 @@
|
||||
*/
|
||||
var Module = require('../../proxy/module');
|
||||
var User = require('../../proxy/user');
|
||||
var eventproxy = require('eventproxy');
|
||||
|
||||
exports.display = function (req, res, next) {
|
||||
var name = req.params.name;
|
||||
exports.display = function *(next) {
|
||||
var name = this.params.name;
|
||||
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(next);
|
||||
Module.listByAuthor(name, ep.done('packages'));
|
||||
User.get(name, ep.done('user'));
|
||||
var r = yield [Module.listByAuthor(name), User.get(name)];
|
||||
var packages = r[0];
|
||||
var user = r[1];
|
||||
if (!user && !packages.length) {
|
||||
return yield* next;
|
||||
}
|
||||
user = {
|
||||
name: name,
|
||||
email: user && user.email
|
||||
};
|
||||
|
||||
ep.all('packages', 'user', function (packages, user) {
|
||||
//because of sync, maybe no this user in database,
|
||||
//but his packages in this registry
|
||||
if (!user && !packages.length) {
|
||||
return next();
|
||||
}
|
||||
user = {
|
||||
name: name,
|
||||
email: user && user.email
|
||||
};
|
||||
|
||||
return res.render('profile', {
|
||||
title: 'User - ' + name,
|
||||
packages: packages || [],
|
||||
user: user
|
||||
});
|
||||
yield this.render('profile', {
|
||||
title: 'User - ' + name,
|
||||
packages: packages || [],
|
||||
user: user
|
||||
});
|
||||
};
|
||||
|
||||
46
dispatch.js
46
dispatch.js
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - dispatch.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
var path = require('path');
|
||||
var util = require('util');
|
||||
var fs = require('fs');
|
||||
var cluster = require('cluster');
|
||||
var config = require('./config');
|
||||
var workerPath = path.join(__dirname, 'worker.js');
|
||||
@@ -25,35 +24,54 @@ var childProcess = require('child_process');
|
||||
var syncPath = path.join(__dirname, 'sync');
|
||||
|
||||
if (config.enableCluster) {
|
||||
forkWorker();
|
||||
if (config.syncModel !== 'none') {
|
||||
forkSyncer();
|
||||
}
|
||||
} else {
|
||||
require(workerPath);
|
||||
if (config.syncModel !== 'none') {
|
||||
require(syncPath);
|
||||
}
|
||||
}
|
||||
|
||||
function forkWorker() {
|
||||
cluster.setupMaster({
|
||||
exec: workerPath
|
||||
});
|
||||
|
||||
cluster.on('fork', function (worker) {
|
||||
console.log('[%s] [worker:%d] new worker start', new Date(), worker.process.pid);
|
||||
console.log('[%s] [worker:%d] new worker start', 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);
|
||||
console.error('[%s] [master:%s] wroker:%s disconnect, suicide: %s, state: %s. New worker:%s fork',
|
||||
Date(), process.pid, worker.process.pid, worker.suicide, worker.state, 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));
|
||||
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(err);
|
||||
console.error('[%s] [master:%s] wroker exit: %s', Date(), process.pid, err.stack);
|
||||
});
|
||||
|
||||
var numCPUs = require('os').cpus().length;
|
||||
// Fork workers.
|
||||
for (var i = 0; i < numCPUs; i++) {
|
||||
for (var i = 0; i < config.numCPUs; i++) {
|
||||
cluster.fork();
|
||||
}
|
||||
|
||||
childProcess.fork(syncPath);
|
||||
} else {
|
||||
require(workerPath);
|
||||
require(syncPath);
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
102
docs/db.sql
102
docs/db.sql
@@ -9,17 +9,53 @@ 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',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`keyword` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'keyword',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`description` longtext,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `keyword_module_name` (`keyword`,`name`),
|
||||
KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module keyword';
|
||||
|
||||
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',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`author` varchar(100) NOT NULL,
|
||||
`name` varchar(100) NOT NULL COMMENT 'module name',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`version` varchar(30) NOT NULL COMMENT 'module version',
|
||||
`description` longtext,
|
||||
`package` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'package.json',
|
||||
@@ -38,30 +74,46 @@ CREATE TABLE `module` (
|
||||
-- ALTER TABLE `module` CHANGE `package` `package` LONGTEXT CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||
-- ALTER TABLE `module` CHANGE `description` `description` LONGTEXT CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||
-- show create table module\G
|
||||
-- ALTER TABLE `module` CHANGE `name` `name` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
|
||||
|
||||
CREATE TABLE `module_log` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`username` varchar(100) NOT NULL,
|
||||
`name` varchar(100) NOT NULL COMMENT 'module name',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`log` longtext,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module sync log';
|
||||
-- ALTER TABLE `module_log` CHANGE `name` `name` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
|
||||
|
||||
CREATE TABLE `tag` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`name` varchar(100) NOT NULL COMMENT 'module name',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`tag` varchar(30) NOT NULL COMMENT 'tag name',
|
||||
`version` varchar(30) NOT NULL COMMENT 'module version',
|
||||
`module_id` bigint(20) unsigned NOT NULL COMMENT 'module id',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `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',
|
||||
@@ -77,7 +129,7 @@ CREATE TABLE `total` (
|
||||
`last_sync_module` varchar(100) COMMENT 'last sync success module name',
|
||||
PRIMARY KEY (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='total info';
|
||||
INSERT INTO total(name, gmt_modified, module_delete) VALUES('total', now(), 0, 0);
|
||||
INSERT INTO total(name, gmt_modified) VALUES('total', now());
|
||||
-- ALTER TABLE `total` ADD `last_sync_time` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'last timestamp sync from official registry'
|
||||
-- ALTER TABLE `total` ADD `last_exist_sync_time` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'last timestamp sync exist packages from official registry'
|
||||
-- ALTER TABLE `total` ADD `sync_status` tinyint unsigned NOT NULL DEFAULT '0' COMMENT 'system sync from official registry status'
|
||||
@@ -92,8 +144,46 @@ CREATE TABLE `download_total` (
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`gmt_modified` datetime NOT NULL COMMENT 'modified time',
|
||||
`date` varchar(10) NOT NULL COMMENT 'YYYY-MM-DD format',
|
||||
`name` varchar(100) NOT NULL COMMENT 'module name',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`count` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'download count',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `date_name` (`date`, `name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module download total info';
|
||||
-- ALTER TABLE `download_total` CHANGE `name` `name` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name';
|
||||
|
||||
CREATE TABLE `module_deps` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key',
|
||||
`gmt_create` datetime NOT NULL COMMENT 'create time',
|
||||
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name',
|
||||
`deps` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'which module depend on this module',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name_deps` (`name`,`deps`),
|
||||
KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module deps';
|
||||
|
||||
CREATE TABLE `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';
|
||||
@@ -91,23 +91,25 @@ $ open http://localhost:7001
|
||||
$ open http://localhost:7002
|
||||
```
|
||||
|
||||
## use cnpm cli with your own registry
|
||||
You do not need to write another command line tool with your own registry,
|
||||
## use cnpm cli with your own registry
|
||||
You do not need to write another command line tool with your own registry,
|
||||
just alias [cnpm](http://github.com/fengmk2/cnpm), then you can get a npm client for you own registry.
|
||||
|
||||
```
|
||||
# install cnpm first
|
||||
npm install -g cnpm
|
||||
npm install -g cnpm
|
||||
|
||||
# then alias lnpm to cnpm, but change config to your own registry
|
||||
alias lnpm='cnpm --registry=http://localhost:7001\
|
||||
--registryweb=http://localhost:7002\
|
||||
--cache=$HOME/.npm/.cache/lnpm\
|
||||
--disturl=http://cnpmjs.org/dist\
|
||||
--userconfig=$HOME/.lnpmrc'
|
||||
|
||||
#or put this in .zshrc or .bashrc
|
||||
echo "#lnpm alias\nalias lnpm='cnpm --registry=http://localhost:7001\
|
||||
--registryweb=http://localhost:7002\
|
||||
--cache=$HOME/.npm/.cache/lnpm\
|
||||
--disturl=http://cnpmjs.org/dist\
|
||||
--userconfig=$HOME/.lnpmrc'" >> $HOME/.zshrc && source $HOME/.zshrc
|
||||
```
|
||||
|
||||
@@ -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/fengmk2/cnpmjs.org) [](https://coveralls.io/r/fengmk2/cnpmjs.org)[](https://gemnasium.com/fengmk2/cnpmjs.org)
|
||||
|
||||
[](https://nodei.co/npm/cnpmjs.org/)
|
||||
|
||||
## What is this?
|
||||
|
||||
> Private npm registry and web for Enterprise, base on MySQL and Simple File Store.
|
||||
|
||||
@[JacksonTian](https://github.com/JacksonTian/) had a talk about [private npm](https://speakerdeck.com/jacksontian/qi-ye-ji-node-dot-jskai-fa).
|
||||
|
||||
## Install your private npm registry
|
||||
|
||||
@see [Install and Get Started](/install).
|
||||
So `cnpm` is meaning: **Company npm**.
|
||||
|
||||
## Registry
|
||||
|
||||
* Our public registry: [registry.cnpmjs.org](http://registry.cnpmjs.org)
|
||||
* 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,30 +104,40 @@ $(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://registry.cnpmjs.org \
|
||||
alias cnpm="npm --registry=http://r.cnpmjs.org \
|
||||
--cache=$HOME/.npm/.cache/cnpm \
|
||||
--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://registry.cnpmjs.org \
|
||||
$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=http://r.cnpmjs.org \
|
||||
--cache=$HOME/.npm/.cache/cnpm \
|
||||
--disturl=http://cnpmjs.org/dist \
|
||||
--userconfig=$HOME/.cnpmrc"' >> ~/.zshrc && source ~/.zshrc
|
||||
```
|
||||
|
||||
Or you can just use our `cnpm` cli:
|
||||
### install
|
||||
|
||||
```bash
|
||||
$ npm install cnpm -g
|
||||
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]
|
||||
```
|
||||
|
||||
### sync
|
||||
|
||||
Only `cnpm` cli has this command:
|
||||
Only `cnpm` cli has this command. Meaning sync package from source npm.
|
||||
|
||||
```bash
|
||||
$ cnpm sync connect
|
||||
@@ -149,43 +149,36 @@ sync package on web: [cnpmjs.org/sync/connect](http://cnpmjs.org/sync/connect)
|
||||
$ open http://cnpmjs.org/sync/connect
|
||||
```
|
||||
|
||||
### publish
|
||||
### publish / unpublish
|
||||
|
||||
Meaning sync package from source npm.
|
||||
|
||||
Only `admin` user can publish package to private registry.
|
||||
Only `admin` user can publish / unpublish package to private registry.
|
||||
|
||||
```bash
|
||||
$ cnpm publish [name]
|
||||
$ cnpm unpublish [name]
|
||||
```
|
||||
|
||||
### Other commands
|
||||
|
||||
Support all the other npm commands. e.g.:
|
||||
|
||||
```bash
|
||||
$ cnpm info cnpm
|
||||
```
|
||||
|
||||
## TODO list
|
||||
|
||||
@see Github [Issues](https://github.com/fengmk2/cnpmjs.org/issues)
|
||||
@see Github [Issues](https://github.com/cnpm/cnpmjs.org/issues)
|
||||
|
||||
## Authors
|
||||
## Histories
|
||||
|
||||
```bash
|
||||
$ git summary
|
||||
|
||||
project : cnpmjs.org
|
||||
repo age : 2 weeks
|
||||
active : 73 days
|
||||
commits : 186
|
||||
files : 83
|
||||
authors :
|
||||
109 fengmk2 58.6%
|
||||
77 dead_horse 41.4%
|
||||
```
|
||||
Release [History](/history).
|
||||
|
||||
## npm and cnpm relation
|
||||
|
||||

|
||||
|
||||
## 捐赠 Donate
|
||||
如果您觉得 [cnpmjs.org] 对您有帮助,欢迎请作者一杯咖啡.
|
||||
如果您觉得 [cnpmjs.org](/) 对您有帮助,欢迎请作者一杯咖啡.
|
||||
|
||||
[](https://me.alipay.com/imk2)
|
||||
|
||||
[cnpmjs.org]: http://cnpmjs.org/
|
||||
[registry.cnpmjs.org]: http://registry.cnpmjs.org/
|
||||
|
||||
30
index.js
Normal file
30
index.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/**!
|
||||
* cnpmjs.org - index.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var config = require('./config');
|
||||
|
||||
exports.loadConfig = config.loadConfig;
|
||||
exports.config = config;
|
||||
|
||||
exports.startWorker = function (customConfig) {
|
||||
config.loadConfig(customConfig);
|
||||
require('./worker');
|
||||
};
|
||||
|
||||
exports.startSync = function (customConfig) {
|
||||
config.loadConfig(customConfig);
|
||||
require('./sync');
|
||||
};
|
||||
@@ -17,9 +17,11 @@
|
||||
var crypto = require('crypto');
|
||||
var path = require('path');
|
||||
var config = require('../config');
|
||||
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);
|
||||
};
|
||||
@@ -28,8 +30,29 @@ exports.getCDNKey = function (name, filename) {
|
||||
return '/' + name + '/-/' + filename;
|
||||
};
|
||||
|
||||
exports.downloadURL = function (pkg, req) {
|
||||
if (pkg.dist && pkg.dist.tarball) {
|
||||
pkg.dist.tarball = 'http://' + req.headers.host + '/' + pkg.name + '/download/' + path.basename(pkg.dist.tarball);
|
||||
exports.setDownloadURL = function (pkg, ctx, host) {
|
||||
if (pkg.dist) {
|
||||
host = host || ctx.host;
|
||||
pkg.dist.tarball = util.format('%s://%s/%s/download/%s-%s.tgz',
|
||||
ctx.protocol,
|
||||
host, pkg.name, pkg.name, pkg.version);
|
||||
}
|
||||
};
|
||||
|
||||
exports.isAdmin = function (username) {
|
||||
return typeof config.admins[username] === 'string';
|
||||
};
|
||||
|
||||
exports.isMaintainer = function (user, maintainers) {
|
||||
if (user.isAdmin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var username = user.name;
|
||||
maintainers = maintainers || [];
|
||||
var match = maintainers.filter(function (item) {
|
||||
return item.name === username;
|
||||
});
|
||||
|
||||
return match.length > 0;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - middleware/auth.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -17,47 +17,44 @@
|
||||
var debug = require('debug')('cnpmjs.org:middleware:auth');
|
||||
var User = require('../proxy/user');
|
||||
var config = require('../config');
|
||||
var common = require('../lib/common');
|
||||
|
||||
module.exports = function (options) {
|
||||
return function auth(req, res, next) {
|
||||
req.session.onlySync = config.enablePrivate ? true : false;
|
||||
if (req.session.name) {
|
||||
if (config.admins[req.session.name]) {
|
||||
req.session.isAdmin = true;
|
||||
}
|
||||
debug('auth exists user: %s, onlySync: %s, isAdmin: %s, headers: %j',
|
||||
req.session.name, req.session.onlySync, req.session.isAdmin, req.headers);
|
||||
return next();
|
||||
return function *auth(next) {
|
||||
var session = yield *this.session;
|
||||
debug('%s, %s, %j', this.url, this.sessionId, session);
|
||||
this.user = {};
|
||||
|
||||
if (session.name) {
|
||||
this.user.name = session.name;
|
||||
this.user.isAdmin = common.isAdmin(session.name);
|
||||
debug('auth exists user: %j, headers: %j', this.user, this.header);
|
||||
return yield *next;
|
||||
}
|
||||
var authorization = (req.headers.authorization || '').split(' ')[1] || '';
|
||||
|
||||
var authorization = (this.get('authorization') || '').split(' ')[1] || '';
|
||||
authorization = authorization.trim();
|
||||
if (!authorization) {
|
||||
return next();
|
||||
return yield *next;
|
||||
}
|
||||
|
||||
authorization = new Buffer(authorization, 'base64').toString().split(':');
|
||||
if (authorization.length !== 2) {
|
||||
return yield *next;
|
||||
}
|
||||
|
||||
var username = authorization[0];
|
||||
var password = authorization[1];
|
||||
|
||||
User.auth(username, password, function (err, row) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
var row = yield User.auth(username, password);
|
||||
if (!row) {
|
||||
debug('auth fail user: %j, headers: %j', row, this.header);
|
||||
return yield *next;
|
||||
}
|
||||
|
||||
if (!row) {
|
||||
debug('auth fail user: %j, headers: %j', row, req.headers);
|
||||
return res.json(401, {
|
||||
error: 'unauthorized',
|
||||
reason: 'Name or password is incorrect.'
|
||||
});
|
||||
}
|
||||
|
||||
req.session.name = row.name;
|
||||
if (config.admins[req.session.name]) {
|
||||
req.session.isAdmin = true;
|
||||
}
|
||||
debug('auth pass user: %j, onlySync: %s, isAdmin: %s, headers: %j',
|
||||
row, req.session.onlySync, req.session.isAdmin, req.headers);
|
||||
next();
|
||||
});
|
||||
this.user.name = row.name;
|
||||
this.user.isAdmin = common.isAdmin(row.name);
|
||||
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);
|
||||
}
|
||||
@@ -14,12 +14,14 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
module.exports = function login(req, res, next) {
|
||||
if (!req.session.name) {
|
||||
return res.json(401, {
|
||||
module.exports = function *login(next) {
|
||||
if (!this.user.name) {
|
||||
this.status = 401;
|
||||
this.body = {
|
||||
error: 'unauthorized',
|
||||
reason: 'Login first.'
|
||||
});
|
||||
};
|
||||
return;
|
||||
}
|
||||
next();
|
||||
yield *next;
|
||||
};
|
||||
|
||||
@@ -24,8 +24,11 @@ var template = '<?xml version="1.0" encoding="UTF-8"?>\
|
||||
|
||||
var lastModifyDate = new Date();
|
||||
|
||||
module.exports = function publishable(req, res, next) {
|
||||
res.charset = res.charset || 'utf-8';
|
||||
res.setHeader('Content-Type', 'text/xml');
|
||||
res.send(template.replace('${host}', req.headers.host));
|
||||
module.exports = function *publishable(next) {
|
||||
if (this.path === '/opensearch.xml') {
|
||||
this.type = 'text/xml';
|
||||
this.charset = 'utf-8';
|
||||
this.body = template.replace('${host}', this.host);
|
||||
}
|
||||
yield *next;
|
||||
};
|
||||
|
||||
@@ -14,13 +14,17 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
module.exports = function publishable(req, res, next) {
|
||||
if (req.session.onlySync && !req.session.isAdmin) {
|
||||
var config = require('../config');
|
||||
|
||||
module.exports = function *publishable(next) {
|
||||
if (config.enablePrivate && !this.user.isAdmin) {
|
||||
// private mode, only admin user can publish
|
||||
return res.json(403, {
|
||||
this.status = 403;
|
||||
this.body = {
|
||||
error: 'no_perms',
|
||||
reason: 'Private mode enable, only admin can publish this module'
|
||||
});
|
||||
};
|
||||
return;
|
||||
}
|
||||
next();
|
||||
yield *next;
|
||||
};
|
||||
|
||||
32
middleware/registry_not_found.js
Normal file
32
middleware/registry_not_found.js
Normal file
@@ -0,0 +1,32 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/registry_not_found.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
module.exports = function *notFound(next) {
|
||||
yield *next;
|
||||
|
||||
if (this.status && this.status !== 404) {
|
||||
return;
|
||||
}
|
||||
if (this.body && this.body.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.status = 404;
|
||||
this.body = {
|
||||
error: 'not_found',
|
||||
reason: 'document not found'
|
||||
};
|
||||
};
|
||||
29
middleware/static.js
Normal file
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.
|
||||
@@ -17,24 +17,25 @@
|
||||
var config = require('../config');
|
||||
|
||||
/**
|
||||
* req.session.allowSync - allow sync triggle by cnpm install
|
||||
* this.allowSync - allow sync triggle by cnpm install
|
||||
*/
|
||||
|
||||
module.exports = function (req, res, next) {
|
||||
module.exports = function* syncByInstall(next) {
|
||||
if (!config.syncByInstall || !config.enablePrivate) {
|
||||
// only config.enablePrivate should enable sync on install
|
||||
return next();
|
||||
return yield* next;
|
||||
}
|
||||
// request not by node, consider it request from web
|
||||
if (req.headers['user-agent'] && req.headers['user-agent'].indexOf('node') !== 0) {
|
||||
return next();
|
||||
var ua = this.get('user-agent');
|
||||
if (!ua || ua.indexOf('node') < 0) {
|
||||
return yield* next;
|
||||
}
|
||||
|
||||
req.session.allowSync = true;
|
||||
if (req.session.isAdmin) {
|
||||
// if current user is admin, should not enable auto sync on install, because it would be unpublish
|
||||
req.session.allowSync = false;
|
||||
// if request with `/xxx?write=true`, meaning the read request using for write
|
||||
if (this.query.write) {
|
||||
return yield* next;
|
||||
}
|
||||
|
||||
next();
|
||||
this.allowSync = true;
|
||||
yield* next;
|
||||
};
|
||||
|
||||
49
middleware/web_not_found.js
Normal file
49
middleware/web_not_found.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/**!
|
||||
* cnpmjs.org - middleware/web_not_found.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com> (http://deadhorse.me)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:middleware:web_not_found');
|
||||
|
||||
module.exports = function *notFound(next) {
|
||||
yield *next;
|
||||
|
||||
if (this.status && this.status !== 404) {
|
||||
return;
|
||||
}
|
||||
if (this.body) {
|
||||
return;
|
||||
}
|
||||
|
||||
var m = /^\/([\w\-\_\.]+)\/?$/.exec(this.url);
|
||||
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.body = 'Cannot GET ' + this.path;
|
||||
};
|
||||
117
package.json
117
package.json
@@ -1,79 +1,86 @@
|
||||
{
|
||||
"name": "cnpmjs.org",
|
||||
"version": "0.1.2",
|
||||
"description": "Private npm registry and web",
|
||||
"main": "dispatch.js",
|
||||
"version": "0.8.0",
|
||||
"description": "Private npm registry and web for Enterprise, base on MySQL and Simple Store Service",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "make test-all",
|
||||
"start": "./bin/nodejsctl start && npm run status",
|
||||
"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 && npm run status",
|
||||
"restart": "./bin/nodejsctl restart && npm run status",
|
||||
"blanket": {
|
||||
"pattern": "//^((?!(node_modules|test|common)).)*$/",
|
||||
"data-cover-flags": {
|
||||
"debug": false
|
||||
}
|
||||
},
|
||||
"travis-cov": {
|
||||
"threshold": 90
|
||||
}
|
||||
"stop": "./bin/nodejsctl stop"
|
||||
},
|
||||
"dependencies": {
|
||||
"humanize-number": "0.0.2",
|
||||
"bytes": "1.0.0",
|
||||
"cheerio": "0.17.0",
|
||||
"co": "3.0.6",
|
||||
"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",
|
||||
"urllib": "0.5.5",
|
||||
"connect-markdown": "0.0.3",
|
||||
"qn": "0.2.0",
|
||||
"microtime": "0.5.1",
|
||||
"debug": "0.7.4",
|
||||
"utility": "0.1.9",
|
||||
"ready": "0.1.1",
|
||||
"connect": "2.12.0",
|
||||
"connect-rt": "0.0.2",
|
||||
"connect-redis": "1.4.6",
|
||||
"connect-render": "0.3.2",
|
||||
"urlrouter": "0.5.4",
|
||||
"graceful": "0.0.5",
|
||||
"moment": "2.4.0",
|
||||
"logfilestream": "0.1.0",
|
||||
"humanize-number": "0.0.2",
|
||||
"koa": "0.8.1",
|
||||
"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.7.0",
|
||||
"ms": "0.6.2",
|
||||
"mkdirp": "0.3.5",
|
||||
"mysql": "2.0.0-rc2",
|
||||
"response-patch": "0.1.1",
|
||||
"response-cookie": "0.0.2",
|
||||
"bagpipe": "0.3.5",
|
||||
"semver": "2.2.1",
|
||||
"marked": "0.2.10",
|
||||
"nodemailer": "0.5.15",
|
||||
"eventproxy": "0.2.6"
|
||||
"multiline": "0.3.4",
|
||||
"mysql": "2.4.1",
|
||||
"nodemailer": "0.7.1",
|
||||
"qn": "0.2.2",
|
||||
"ready": "0.1.1",
|
||||
"redis": "0.11.0",
|
||||
"semver": "2.3.1",
|
||||
"thunkify-wrap": "1.0.1",
|
||||
"utility": "0.1.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"supertest": "*",
|
||||
"should": "*",
|
||||
"blanket": "*",
|
||||
"travis-cov": "*",
|
||||
"coveralls": "*",
|
||||
"mocha-lcov-reporter": "*",
|
||||
"autod": "~0.2.0",
|
||||
"chunkstream": "0.0.1",
|
||||
"co-mocha": "0.0.2",
|
||||
"contributors": "*",
|
||||
"cov": "*",
|
||||
"istanbul-harmony": "*",
|
||||
"jshint": "*",
|
||||
"mm": "0.2.1",
|
||||
"mocha": "*",
|
||||
"mm": "*",
|
||||
"pedding": "*"
|
||||
"pedding": "1.0.0",
|
||||
"should": "4.0.4",
|
||||
"should-http": "0.0.1",
|
||||
"supertest": "0.13.0"
|
||||
},
|
||||
"homepage": "https://github.com/fengmk2/cnpmjs.org",
|
||||
"homepage": "https://github.com/cnpm/cnpmjs.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/fengmk2/cnpmjs.org.git",
|
||||
"web": "https://github.com/fengmk2/cnpmjs.org"
|
||||
"url": "git://github.com/cnpm/cnpmjs.org.git",
|
||||
"web": "https://github.com/cnpm/cnpmjs.org"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/fengmk2/cnpmjs.org/issues",
|
||||
"url": "https://github.com/cnpm/cnpmjs.org/issues",
|
||||
"email": "fengmk2@gmail.com"
|
||||
},
|
||||
"keywords": [
|
||||
"cnpmjs.org", "npm", "npmjs", "npmjs.org", "registry"
|
||||
"cnpmjs.org",
|
||||
"npm",
|
||||
"npmjs",
|
||||
"npmjs.org",
|
||||
"registry"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
"node": ">= 0.11.9"
|
||||
},
|
||||
"author": [
|
||||
"fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)",
|
||||
|
||||
85
proxy/dist.js
Normal file
85
proxy/dist.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/**!
|
||||
* cnpmjs.org - proxy/dist.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 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]);
|
||||
};
|
||||
@@ -14,30 +14,49 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
526
proxy/module.js
526
proxy/module.js
@@ -14,24 +14,34 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
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, author, name, version, package, dist_tarball, dist_shasum, dist_size) \
|
||||
// VALUES(now(), now(), ?, ?, ?, ?, ?, ?, ?);';
|
||||
|
||||
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;
|
||||
var pkg;
|
||||
try {
|
||||
pkg = stringifyPackage(mod.package);
|
||||
@@ -54,16 +64,124 @@ exports.add = function (mod, callback) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, {id: result.insertId, gmt_modified: new Date()});
|
||||
|
||||
if (typeof keywords === 'string') {
|
||||
keywords = [keywords];
|
||||
}
|
||||
|
||||
if (!Array.isArray(keywords)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var words = [];
|
||||
for (var i = 0; i < keywords.length; i++) {
|
||||
var w = keywords[i];
|
||||
if (typeof w === 'string') {
|
||||
w = w.trim();
|
||||
if (w) {
|
||||
words.push(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (words.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add keywords
|
||||
exports.addKeywords(mod, description, words, utility.noop);
|
||||
});
|
||||
};
|
||||
|
||||
var UPDATE_DESC_SQL = 'UPDATE module SET description=? WHERE id=?;';
|
||||
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) {
|
||||
var keywords = [];
|
||||
if (rows && rows.length) {
|
||||
keywords = rows.map(function (r) {
|
||||
return r.keyword;
|
||||
});
|
||||
}
|
||||
callback(err, keywords);
|
||||
});
|
||||
};
|
||||
|
||||
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 = '';
|
||||
var values = [];
|
||||
for (var i = 0; i < keywords.length; i++) {
|
||||
sql += ADD_KEYWORD_SQL;
|
||||
values.push(keywords[i]);
|
||||
values.push(name);
|
||||
values.push(description);
|
||||
}
|
||||
mysql.query(sql, values, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var ids = [];
|
||||
for (var i = 0; i < results.length; i++) {
|
||||
var r = results[i];
|
||||
if (r.insertId) {
|
||||
ids.push(r.insertId);
|
||||
}
|
||||
}
|
||||
callback(null, ids);
|
||||
});
|
||||
};
|
||||
|
||||
var UPDATE_DESC_SQL = multiline(function () {;/*
|
||||
UPDATE
|
||||
module
|
||||
SET
|
||||
description=?
|
||||
WHERE
|
||||
id=?;
|
||||
*/});
|
||||
exports.updateDescription = function (id, description, callback) {
|
||||
mysql.query(UPDATE_DESC_SQL, [description, id], callback);
|
||||
};
|
||||
|
||||
var UPDATE_DIST_SQL = 'UPDATE module SET version=?, package=?, dist_tarball=?, dist_shasum=?, dist_size=? 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) {
|
||||
return callback(err);
|
||||
}
|
||||
data.package = data.package || {};
|
||||
data.package.readme = readme;
|
||||
var pkg = stringifyPackage(data.package);
|
||||
mysql.query(UPDATE_PACKAGE_SQL, [pkg, id], callback);
|
||||
});
|
||||
};
|
||||
|
||||
var UPDATE_DIST_SQL = 'UPDATE module SET ? WHERE id=?';
|
||||
exports.update = function (mod, callback) {
|
||||
var pkg;
|
||||
try {
|
||||
@@ -72,7 +190,18 @@ exports.update = function (mod, callback) {
|
||||
return callback(e);
|
||||
}
|
||||
var dist = mod.package.dist;
|
||||
mysql.query(UPDATE_DIST_SQL, [mod.version, pkg, dist.tarball, dist.shasum, dist.size, mod.id],
|
||||
|
||||
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,
|
||||
[arg, mod.id],
|
||||
function (err, result) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
@@ -94,13 +223,22 @@ function parseRow(row) {
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.parseRow = parseRow;
|
||||
|
||||
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) {
|
||||
@@ -117,8 +255,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) {
|
||||
@@ -134,14 +279,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) {
|
||||
@@ -157,8 +314,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) {
|
||||
@@ -168,21 +331,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 SELECT_ALL_TAGS_SQL = 'SELECT tag, version, gmt_modified, module_id FROM tag WHERE name=?;';
|
||||
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 = 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) {
|
||||
@@ -205,8 +398,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) {
|
||||
@@ -226,59 +428,103 @@ exports.listByName = function (name, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var LIST_SINCE_SQLS = [
|
||||
'SELECT distinct(name) AS name FROM module WHERE publish_time > ?;',
|
||||
'SELECT module_id FROM tag WHERE tag="latest" AND name IN (?);',
|
||||
'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], [start], ep.done(function (rows) {
|
||||
if (!rows || rows.length === 0) {
|
||||
return callback(null, []);
|
||||
}
|
||||
ep.emit('names', rows.map(function (r) {
|
||||
return r.name;
|
||||
}));
|
||||
}));
|
||||
|
||||
ep.once('names', function (names) {
|
||||
mysql.query(LIST_SINCE_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.once('ids', function (ids) {
|
||||
mysql.query(LIST_SINCE_SQLS[2], [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 DELETE_MODULE_BY_NAME_SQL = 'DELETE FROM module WHERE 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 = 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'
|
||||
];
|
||||
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
|
||||
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 ep = eventproxy.create();
|
||||
ep.fail(callback);
|
||||
@@ -294,7 +540,7 @@ exports.listByAuthor = function (author, callback) {
|
||||
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;
|
||||
}));
|
||||
@@ -305,26 +551,128 @@ exports.listByAuthor = function (author, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
var SEARCH_SQLS = [
|
||||
'SELECT module_id FROM tag WHERE name LIKE ? AND tag="latest" ORDER BY name LIMIT 100;',
|
||||
'SELECT name, description FROM module WHERE id IN (?) ORDER BY name;'
|
||||
];
|
||||
exports.search = function (word, callback) {
|
||||
word = word.replace(/^%/, '') + '%'; //ignore prefix %
|
||||
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;
|
||||
options = null;
|
||||
}
|
||||
options = options || {};
|
||||
var limit = options.limit || 100;
|
||||
word = word.replace(/^%/, ''); //ignore prefix %
|
||||
var ep = eventproxy.create();
|
||||
ep.fail(callback);
|
||||
mysql.query(SEARCH_SQLS[0], [word], ep.done(function (rows) {
|
||||
if (!rows || rows.length === 0) {
|
||||
return callback(null, []);
|
||||
|
||||
// search flows:
|
||||
// 1. prefix search by name
|
||||
// 2. like search by name
|
||||
// 3. keyword equal search
|
||||
var ids = {};
|
||||
|
||||
mysql.query(SEARCH_MODULES_SQL, [word + '%', limit ], ep.done(function (rows) {
|
||||
rows = rows || [];
|
||||
if (rows.length > 0) {
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
ids[rows[i].module_id] = 1;
|
||||
}
|
||||
}
|
||||
ep.emit('ids', rows.map(function (r) {
|
||||
return r.module_id;
|
||||
}));
|
||||
if (rows.length >= 20) {
|
||||
return ep.emit('ids', Object.keys(ids));
|
||||
}
|
||||
|
||||
mysql.query(SEARCH_MODULES_SQL, [ '%' + word + '%', limit ], ep.done('likeSearch'));
|
||||
}));
|
||||
|
||||
ep.on('ids', function (ids) {
|
||||
mysql.query(SEARCH_SQLS[1], [ids], ep.done(function (modules) {
|
||||
callback(null, modules);
|
||||
mysql.query(SEARCH_MODULES_BY_KEYWORD_SQL, [ word, limit ], ep.done('keywordRows'));
|
||||
|
||||
ep.on('likeSearch', function (rows) {
|
||||
rows = rows || [];
|
||||
if (rows.length > 0) {
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
ids[rows[i].module_id] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
ep.emit('ids', Object.keys(ids));
|
||||
});
|
||||
|
||||
ep.all('ids', 'keywordRows', function (ids, keywordRows) {
|
||||
keywordRows = keywordRows || [];
|
||||
var data = {
|
||||
keywordMatchs: keywordRows,
|
||||
searchMatchs: []
|
||||
};
|
||||
if (ids.length === 0) {
|
||||
return callback(null, data);
|
||||
}
|
||||
|
||||
mysql.query(QUERY_MODULES_BY_ID_SQL, [ids], ep.done(function (modules) {
|
||||
data.searchMatchs = modules;
|
||||
callback(null, data);
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
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]);
|
||||
};
|
||||
|
||||
58
proxy/module_deps.js
Normal file
58
proxy/module_deps.js
Normal file
@@ -0,0 +1,58 @@
|
||||
/**!
|
||||
* cnpmjs.org - proxy/module_deps.js
|
||||
*
|
||||
* Copyright(c) 2014
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var mysql = require('../common/mysql');
|
||||
var multiline = require('multiline');
|
||||
|
||||
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 = 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') {
|
||||
err = null;
|
||||
}
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
@@ -14,13 +14,20 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -28,7 +35,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) {
|
||||
@@ -36,7 +43,9 @@ 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);
|
||||
};
|
||||
|
||||
thunkify(exports);
|
||||
|
||||
81
proxy/module_maintainer.js
Normal file
81
proxy/module_maintainer.js
Normal file
@@ -0,0 +1,81 @@
|
||||
/**!
|
||||
* 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]);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
var tasks = [];
|
||||
for (var i = 0; i < addUsers.length; i++) {
|
||||
tasks.push(add(name, addUsers[i]));
|
||||
}
|
||||
yield tasks;
|
||||
// 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
|
||||
};
|
||||
};
|
||||
73
proxy/module_star.js
Normal file
73
proxy/module_star.js
Normal file
@@ -0,0 +1,73 @@
|
||||
/**!
|
||||
* 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';
|
||||
|
||||
/**
|
||||
* 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;
|
||||
});
|
||||
};
|
||||
43
proxy/module_unpublished.js
Normal file
43
proxy/module_unpublished.js
Normal file
@@ -0,0 +1,43 @@
|
||||
/**!
|
||||
* 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';
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
87
proxy/npm.js
87
proxy/npm.js
@@ -14,46 +14,67 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
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 = {
|
||||
dataType: 'json',
|
||||
timeout: 10000
|
||||
};
|
||||
}
|
||||
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, callback);
|
||||
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();
|
||||
}
|
||||
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 (callback) {
|
||||
request('/-/short', {
|
||||
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;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,16 +14,26 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var thunkify = require('thunkify-wrap');
|
||||
var eventproxy = require('eventproxy');
|
||||
var config = require('../config');
|
||||
var mysql = require('../common/mysql');
|
||||
var eventproxy = require('eventproxy');
|
||||
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;';
|
||||
@@ -83,7 +93,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);
|
||||
};
|
||||
@@ -92,29 +109,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);
|
||||
|
||||
115
proxy/user.js
115
proxy/user.js
@@ -14,36 +14,43 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
function passwordSha(password, salt) {
|
||||
return sha1(password + salt);
|
||||
}
|
||||
exports.passwordSha = passwordSha;
|
||||
var multiline = require('multiline');
|
||||
|
||||
var SELECT_USER_SQL = multiline(function () {;/*
|
||||
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, callback) {
|
||||
mysql.queryOne(SELECT_USER_SQL, [name], function (err, row) {
|
||||
if (row) {
|
||||
try {
|
||||
row.roles = JSON.parse(row.roles);
|
||||
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;
|
||||
}
|
||||
}
|
||||
callback(err, row);
|
||||
});
|
||||
};
|
||||
|
||||
function passwordSha(password, salt) {
|
||||
return utility.sha1(password + salt);
|
||||
}
|
||||
|
||||
exports.auth = function (name, password, callback) {
|
||||
exports.get(name, function (err, row) {
|
||||
if (err || !row) {
|
||||
@@ -58,9 +65,8 @@ exports.auth = function (name, password, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
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 +75,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,12 +121,52 @@ 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;
|
||||
|
||||
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]);
|
||||
}
|
||||
};
|
||||
|
||||
var LIST_BY_NAMES_SQL = multiline(function () {;/*
|
||||
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]);
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var limit = require('../middleware/limit');
|
||||
var login = require('../middleware/login');
|
||||
var publishable = require('../middleware/publishable');
|
||||
var syncByInstall = require('../middleware/sync_by_install');
|
||||
@@ -24,7 +25,15 @@ var user = require('../controllers/registry/user');
|
||||
var sync = require('../controllers/sync');
|
||||
|
||||
function routes(app) {
|
||||
app.get('/', 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
|
||||
@@ -34,36 +43,37 @@ function routes(app) {
|
||||
app.get('/-/short', mod.listAllModuleNames);
|
||||
|
||||
// module
|
||||
app.get('/:name', [syncByInstall], mod.show);
|
||||
app.get('/:name/:version', [syncByInstall], mod.get);
|
||||
// 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('/: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('/:name/:tag', login, mod.updateTag);
|
||||
|
||||
// need limit by ip
|
||||
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('/:name/download/:filename/-rev/:rev', [login, publishable], mod.removeTar);
|
||||
|
||||
// put package.json to module
|
||||
app.put('/:name/:version/-tag/latest', [login, publishable], mod.updateLatest);
|
||||
app.delete('/:name/download/:filename/-rev/:rev', login, publishable, mod.removeTar);
|
||||
|
||||
// update module, unpublish will PUT this
|
||||
app.put('/:name/-rev/:rev', [login, publishable], mod.removeWithVersions);
|
||||
app.delete('/:name/-rev/:rev', [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
|
||||
// https://registry.npmjs.org/-/user/org.couchdb.user:fengmk2
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - routes/web.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -19,18 +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(/\/browse\/keyword\/(@[\w\-\.]+\/[\w\-\.]+)$/, pkg.search);
|
||||
app.get('/browse/keyword/:word', pkg.search);
|
||||
|
||||
app.get('/~:name', user.display);
|
||||
|
||||
app.get(/\/sync\/(@[\w\-\.]+\/[\w\-\.]+)$/, pkg.displaySync);
|
||||
app.get('/sync/:name', pkg.displaySync);
|
||||
|
||||
app.put(/\/sync\/(@[\w\-\.]+\/[\w\-\.]+)$/, sync.sync);
|
||||
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;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - servers/registry.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -15,64 +15,56 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
require('response-patch');
|
||||
var koa = require('koa');
|
||||
var app = module.exports = koa();
|
||||
var http = require('http');
|
||||
var connect = require('connect');
|
||||
var rt = require('connect-rt');
|
||||
var responseCookie = require('response-cookie');
|
||||
var urlrouter = require('urlrouter');
|
||||
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 app = connect();
|
||||
middlewares.jsonp(app);
|
||||
app.use(middlewares.rt({headerName: 'X-ReadTime'}));
|
||||
app.use(middlewares.rewrite('/favicon.ico', '/favicon.png'));
|
||||
app.use(staticCache);
|
||||
|
||||
app.use(rt({headerName: 'X-ReadTime'}));
|
||||
app.use(function (req, res, next) {
|
||||
res.req = req;
|
||||
next();
|
||||
});
|
||||
|
||||
app.use('/dist', connect.static(config.uploadDir));
|
||||
|
||||
app.use(responseCookie());
|
||||
app.use(connect.cookieParser());
|
||||
app.use(connect.query());
|
||||
app.use(connect.json());
|
||||
app.keys = ['todokey', config.sessionSecret];
|
||||
app.proxy = true;
|
||||
app.use(session);
|
||||
app.use(middlewares.bodyParser({jsonLimit: config.jsonLimit}));
|
||||
app.use(auth());
|
||||
app.use(notFound);
|
||||
|
||||
if (config.enableCompress) {
|
||||
app.use(middlewares.compress({threshold: 150}));
|
||||
}
|
||||
app.use(middlewares.conditional());
|
||||
app.use(middlewares.etag());
|
||||
|
||||
/**
|
||||
* Routes
|
||||
*/
|
||||
|
||||
app.use(urlrouter(routes));
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
res.json(404, {error: 'not_found', reason: 'document not found'});
|
||||
});
|
||||
app.use(middlewares.router(app));
|
||||
routes(app);
|
||||
|
||||
/**
|
||||
* Error handler
|
||||
*/
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
err.url = err.url || req.url;
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
console.error(err.stack);
|
||||
logger.error(err);
|
||||
}
|
||||
if (config.debug) {
|
||||
return next(err);
|
||||
}
|
||||
res.json(500, {
|
||||
error: err.name,
|
||||
reason: err.message
|
||||
});
|
||||
app.on('error', function (err, ctx) {
|
||||
err.url = err.url || ctx.request.url;
|
||||
logger.error(err);
|
||||
});
|
||||
|
||||
app = http.createServer(app);
|
||||
app = http.createServer(app.callback());
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(config.registryPort);
|
||||
}
|
||||
|
||||
module.exports = app;
|
||||
|
||||
106
servers/web.js
106
servers/web.js
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - servers/web.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -15,81 +15,105 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
require('response-patch');
|
||||
var path = require('path');
|
||||
var http = require('http');
|
||||
var connect = require('connect');
|
||||
var rt = require('connect-rt');
|
||||
var urlrouter = require('urlrouter');
|
||||
var connectMarkdown = require('connect-markdown');
|
||||
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');
|
||||
var session = require('../common/session');
|
||||
var render = require('connect-render');
|
||||
var opensearch = require('../middleware/opensearch');
|
||||
var app = connect();
|
||||
|
||||
var app = koa();
|
||||
|
||||
var rootdir = path.dirname(__dirname);
|
||||
|
||||
app.use(rt({headerName: 'X-ReadTime'}));
|
||||
app.use(function (req, res, next) {
|
||||
res.req = req;
|
||||
next();
|
||||
});
|
||||
app.use(middlewares.rt({headerName: 'X-ReadTime'}));
|
||||
app.use(middlewares.rewrite('/favicon.ico', '/favicon.png'));
|
||||
app.use(staticCache);
|
||||
|
||||
app.use('/public', connect.static(path.join(rootdir, 'public')));
|
||||
app.use('/opensearch.xml', opensearch);
|
||||
|
||||
app.use(connect.cookieParser());
|
||||
app.use(opensearch);
|
||||
app.keys = ['todokey', config.sessionSecret];
|
||||
app.proxy = true;
|
||||
app.use(session);
|
||||
app.use(connect.query());
|
||||
app.use(connect.json());
|
||||
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');
|
||||
|
||||
app.use('/', connectMarkdown({
|
||||
var layoutFile = path.join(viewDir, '_layout.html');
|
||||
var footer = config.customFooter || fs.readFileSync(path.join(viewDir, 'footer.html'), 'utf8');
|
||||
var layout = fs.readFileSync(path.join(viewDir, 'layout.html'), 'utf8')
|
||||
.replace('{{footer}}', footer)
|
||||
.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: path.join(viewDir, 'layout.html'),
|
||||
layout: layoutFile,
|
||||
titleHolder: '<%- locals.title %>',
|
||||
bodyHolder: '<%- locals.body %>',
|
||||
indexName: 'readme'
|
||||
indexName: '_readme'
|
||||
}));
|
||||
|
||||
var helpers = {
|
||||
var locals = {
|
||||
config: config
|
||||
};
|
||||
|
||||
app.use(render({
|
||||
middlewares.ejs(app, {
|
||||
root: viewDir,
|
||||
viewExt: '.html',
|
||||
layout: 'layout',
|
||||
viewExt: 'html',
|
||||
layout: '_layout',
|
||||
cache: config.viewCache,
|
||||
helpers: helpers
|
||||
}));
|
||||
debug: config.debug,
|
||||
locals: locals
|
||||
});
|
||||
|
||||
/**
|
||||
* Routes
|
||||
*/
|
||||
|
||||
app.use(urlrouter(routes));
|
||||
app.use(middlewares.router(app));
|
||||
routes(app);
|
||||
|
||||
/**
|
||||
* Error handler
|
||||
*/
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
err.url = err.url || req.url;
|
||||
console.error(err.stack);
|
||||
app.on('error', function (err, ctx) {
|
||||
err.url = err.url || ctx.request.url;
|
||||
logger.error(err);
|
||||
if (config.debug) {
|
||||
return next(err);
|
||||
}
|
||||
res.statusCode = 500;
|
||||
res.send('Server has some problems. :(');
|
||||
});
|
||||
|
||||
app = http.createServer(app);
|
||||
app = http.createServer(app.callback());
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(config.webPort);
|
||||
}
|
||||
|
||||
module.exports = app;
|
||||
|
||||
60
services/package.js
Normal file
60
services/package.js
Normal file
@@ -0,0 +1,60 @@
|
||||
/**!
|
||||
* 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.updateMaintainers = function* (name, usernames) {
|
||||
var rs = yield [
|
||||
ModuleMaintainer.update(name, usernames),
|
||||
Module.updateLastModified(name),
|
||||
];
|
||||
return rs[0];
|
||||
};
|
||||
|
||||
exports.isMaintainer = function* (name, username) {
|
||||
var maintainers = yield* ModuleMaintainer.get(name);
|
||||
if (maintainers.length === 0) {
|
||||
// if not found maintainers, try to get from latest module package info
|
||||
var latestMod = yield Module.getLatest(name);
|
||||
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;
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
167
sync/sync_all.js
167
sync/sync_all.js
@@ -14,16 +14,17 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('cnpmjs.org:sync:sync_all');
|
||||
var eventproxy = require('eventproxy');
|
||||
var ms = require('ms');
|
||||
var utility = require('utility');
|
||||
var config = require('../config');
|
||||
var Status = require('./status');
|
||||
var Npm = require('../proxy/npm');
|
||||
var Total = require('../proxy/total');
|
||||
var eventproxy = require('eventproxy');
|
||||
var SyncModuleWorker = require('../proxy/sync_module_worker');
|
||||
var debug = require('debug')('cnpmjs.org:sync:sync_all');
|
||||
var utility = require('utility');
|
||||
var Status = require('./status');
|
||||
var Module = require('../proxy/module');
|
||||
var ms = require('ms');
|
||||
var thunkify = require('thunkify-wrap');
|
||||
|
||||
function subtract(subtracter, minuend) {
|
||||
subtracter = subtracter || [];
|
||||
@@ -34,7 +35,9 @@ function subtract(subtracter, minuend) {
|
||||
map[name] = true;
|
||||
});
|
||||
subtracter.forEach(function (name) {
|
||||
!map[name] && results.push(name);
|
||||
if (!map[name]) {
|
||||
results.push(name);
|
||||
}
|
||||
});
|
||||
return results;
|
||||
}
|
||||
@@ -52,110 +55,88 @@ 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
|
||||
* @param {String} lastSyncModule
|
||||
*/
|
||||
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
|
||||
* @param {Number} lastSyncTime
|
||||
*/
|
||||
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.listShort(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 = true;
|
||||
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
120
test/controllers/registry/module/scope_package.test.js
Normal file
120
test/controllers/registry/module/scope_package.test.js
Normal file
@@ -0,0 +1,120 @@
|
||||
/**!
|
||||
* 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 app = require('../../../../servers/registry');
|
||||
var utils = require('../../../utils');
|
||||
|
||||
describe('controllers/registry/module/scope_package.test.js', function () {
|
||||
var pkgname = '@cnpm/test-scope-package';
|
||||
var pkgURL = '/@' + encodeURIComponent(pkgname.substring(1));
|
||||
before(function (done) {
|
||||
// add scope package
|
||||
var pkg = utils.getPackage(pkgname, '0.0.1', utils.admin);
|
||||
|
||||
request(app.listen())
|
||||
.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);
|
||||
});
|
||||
});
|
||||
|
||||
it('should get scope package info: /@scope%2Fname', function (done) {
|
||||
request(app.listen())
|
||||
.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.listen())
|
||||
.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.listen())
|
||||
.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.listen())
|
||||
.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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - test/controllers/registry/user.test.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -16,26 +16,45 @@
|
||||
|
||||
var should = require('should');
|
||||
var request = require('supertest');
|
||||
var mm = require('mm');
|
||||
var app = require('../../../servers/registry');
|
||||
var user = require('../../../proxy/user');
|
||||
var mm = require('mm');
|
||||
var mysql = require('../../../common/mysql');
|
||||
|
||||
describe('controllers/registry/user.test.js', function () {
|
||||
before(function (done) {
|
||||
app.listen(0, done);
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
app.close(done);
|
||||
});
|
||||
|
||||
afterEach(mm.restore);
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -47,7 +66,7 @@ describe('controllers/registry/user.test.js', function () {
|
||||
});
|
||||
|
||||
it('should return 500 when mysql error', function (done) {
|
||||
mm.error(user, 'get', 'mock error');
|
||||
mm.error(mysql, 'query', 'mock mysql error');
|
||||
request(app)
|
||||
.get('/-/user/org.couchdb.user:cnpmjstest1')
|
||||
.expect(500, done);
|
||||
@@ -91,7 +110,7 @@ describe('controllers/registry/user.test.js', function () {
|
||||
password_sha: 'password_sha',
|
||||
email: 'email'
|
||||
})
|
||||
.expect(500, done);
|
||||
.expect(500, done);
|
||||
});
|
||||
|
||||
it('should 201 when user.add ok', function (done) {
|
||||
@@ -145,7 +164,12 @@ describe('controllers/registry/user.test.js', function () {
|
||||
ok: true,
|
||||
name: 'name',
|
||||
roles: []
|
||||
}, done);
|
||||
}, function (err, res) {
|
||||
should.not.exist(err);
|
||||
should.exist(res.headers['set-cookie']);
|
||||
res.headers['set-cookie'].join(';').should.containEql('AuthSession=');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -167,19 +191,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) {
|
||||
@@ -188,6 +219,8 @@ 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'
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
@@ -40,9 +40,52 @@ describe('controllers/sync.test.js', function () {
|
||||
describe('sync source npm package', function () {
|
||||
var logIdRegistry;
|
||||
var logIdWeb;
|
||||
it('should sync success', function (done) {
|
||||
|
||||
it('should sync as publish success', function (done) {
|
||||
request(registryApp)
|
||||
.del('/utility/-rev/123')
|
||||
.set('authorization', baseauth)
|
||||
.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')
|
||||
.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 sync as publish 403 when user not admin', function (done) {
|
||||
mm.data(Npm, 'get', require(path.join(fixtures, 'utility.json')));
|
||||
request(registryApp)
|
||||
.put('/utility_unit_test/sync?publish=true&nodeps=true')
|
||||
.expect(403)
|
||||
.expect({
|
||||
error: 'no_perms',
|
||||
reason: 'Only admin can publish'
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should sync through web success', function (done) {
|
||||
mm.data(Npm, 'get', require(path.join(fixtures, 'utility.json')));
|
||||
request(webApp)
|
||||
.put('/sync/utility')
|
||||
.end(function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'logId');
|
||||
logIdWeb = res.body.logId;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should sync through registry 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)
|
||||
@@ -52,14 +95,6 @@ describe('controllers/sync.test.js', function () {
|
||||
logIdRegistry = res.body.logId;
|
||||
done();
|
||||
});
|
||||
request(webApp)
|
||||
.put('/sync/utility')
|
||||
.end(function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('ok', 'logId');
|
||||
logIdWeb = res.body.logId;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should get sync log', function (done) {
|
||||
@@ -81,4 +116,16 @@ 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({
|
||||
"ok":false,
|
||||
"reason":"can not found @cnpm/not-exists-package in the source registry"
|
||||
})
|
||||
.expect(404, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/**!
|
||||
* cnpmjs.org - test/controllers/total.test.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
var should = require('should');
|
||||
var request = require('supertest');
|
||||
var pedding = require('pedding');
|
||||
var registryApp = require('../../servers/registry');
|
||||
var webApp = require('../../servers/web');
|
||||
var pedding = require('pedding');
|
||||
|
||||
describe('controllers/total.test.js', function () {
|
||||
before(function (done) {
|
||||
@@ -44,6 +44,7 @@ describe('controllers/total.test.js', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return total info by jsonp', function (done) {
|
||||
request(registryApp)
|
||||
.get('?callback=totalCallback')
|
||||
@@ -51,7 +52,8 @@ describe('controllers/total.test.js', function () {
|
||||
.expect(/totalCallback\({.*}\)/, done);
|
||||
});
|
||||
});
|
||||
describe('GET /total in web', function () {
|
||||
|
||||
describe.skip('GET /total in web', function () {
|
||||
it('should return total info', function (done) {
|
||||
request(webApp)
|
||||
.get('/total')
|
||||
@@ -62,5 +64,5 @@ describe('controllers/total.test.js', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
141
test/controllers/web/dist.test.js
Normal file
141
test/controllers/web/dist.test.js
Normal file
@@ -0,0 +1,141 @@
|
||||
/**!
|
||||
* 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.listen(0, done);
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
app.close(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,28 +17,94 @@
|
||||
var should = require('should');
|
||||
var request = require('supertest');
|
||||
var mm = require('mm');
|
||||
var path = require('path');
|
||||
var mysql = require('../../../common/mysql');
|
||||
var app = require('../../../servers/web');
|
||||
var registry = require('../../../servers/registry');
|
||||
var pkg = require('../../../controllers/web/package');
|
||||
|
||||
var fixtures = path.join(path.dirname(path.dirname(__dirname)), 'fixtures');
|
||||
|
||||
describe('controllers/web/package.test.js', function () {
|
||||
var baseauth = 'Basic ' + new Buffer('cnpmjstest10:cnpmjstest10').toString('base64');
|
||||
|
||||
before(function (done) {
|
||||
app.listen(0, done);
|
||||
registry.listen(0, function () {
|
||||
var pkg = require(path.join(fixtures, 'package_and_tgz.json'));
|
||||
request(registry)
|
||||
.put('/' + pkg.name)
|
||||
.set('authorization', baseauth)
|
||||
.send(pkg)
|
||||
.expect(201, function () {
|
||||
app.listen(0, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
app.close(done);
|
||||
});
|
||||
|
||||
afterEach(mm.restore);
|
||||
|
||||
describe('GET /_list/search/search', function () {
|
||||
it('should search with "m"', function (done) {
|
||||
request(app)
|
||||
.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');
|
||||
res.body.rows.length.should.above(0);
|
||||
res.body.rows.forEach(function (row) {
|
||||
row.should.have.keys('key', 'count', 'value');
|
||||
row.value.should.have.keys('name', 'description');
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should search with m', function (done) {
|
||||
request(app)
|
||||
.get('/_list/search/search?startkey=m&limit=2')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.have.keys('rows');
|
||||
res.body.rows.length.should.above(0);
|
||||
res.body.rows.forEach(function (row) {
|
||||
row.should.have.keys('key', 'count', 'value');
|
||||
row.value.should.have.keys('name', 'description');
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should search return empty', function (done) {
|
||||
request(app)
|
||||
.get('/_list/search/search?startkey="cddddsdasdaasds"&limit=2')
|
||||
.expect(200, function (err, res) {
|
||||
should.not.exist(err);
|
||||
res.body.should.eql({rows: []});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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) {
|
||||
@@ -51,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>/)
|
||||
@@ -60,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);
|
||||
});
|
||||
});
|
||||
@@ -82,11 +149,18 @@ 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/mk2testmodule?type=json')
|
||||
.expect(200)
|
||||
.expect('content-type', 'application/json; charset=utf-8', done);
|
||||
});
|
||||
|
||||
it('should list no match ok', function (done) {
|
||||
request(app)
|
||||
.get('/browse/keyword/notexistpackage')
|
||||
@@ -99,7 +173,7 @@ describe('controllers/web/package.test.js', function () {
|
||||
request(app)
|
||||
.get('/browse/keyword/notexistpackage')
|
||||
.expect(500)
|
||||
.expect(/MockError: mm mock error/, done);
|
||||
.expect(/Internal Server Error/, done);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -113,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({
|
||||
@@ -121,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({
|
||||
|
||||
99
test/controllers/web/package/scope_package.test.js
Normal file
99
test/controllers/web/package/scope_package.test.js
Normal file
@@ -0,0 +1,99 @@
|
||||
/**!
|
||||
* 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 registry = require('../../../../servers/registry');
|
||||
var web = require('../../../../servers/web');
|
||||
var utils = require('../../../utils');
|
||||
|
||||
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) {
|
||||
// add scope package
|
||||
var pkg = utils.getPackage(pkgname, '0.0.1', utils.admin);
|
||||
|
||||
request(registry.listen())
|
||||
.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.listen())
|
||||
.put(pkgURL)
|
||||
.set('authorization', utils.adminAuth)
|
||||
.send(pkg)
|
||||
.expect(201, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should show scope package info page: /@scope%2Fname', function (done) {
|
||||
request(web.listen())
|
||||
.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.listen())
|
||||
.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.listen())
|
||||
.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 /@scope/name/ 404', function (done) {
|
||||
request(web.listen())
|
||||
.get('/package/' + pkgname + '/')
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
it('should show scope package with version: /@scope/name/0.0.2', function (done) {
|
||||
request(web.listen())
|
||||
.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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -29,8 +29,9 @@ describe('controllers/web/user.test.js', function () {
|
||||
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);
|
||||
});
|
||||
@@ -43,7 +44,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);
|
||||
|
||||
7
test/fixtures/500.txt
vendored
Normal file
7
test/fixtures/500.txt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
Internal routing error
|
||||
|
||||
Sorry, we cannot connect to the intended server.
|
||||
|
||||
We have just been notified of this problem. We will correct it as soon as possible.
|
||||
|
||||
Feel free to contact us if you have any questions: support@iriscouch.com
|
||||
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"}
|
||||
1
test/fixtures/package_and_tgz.json
vendored
Normal file
1
test/fixtures/package_and_tgz.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"_id":"mk2testmodule","name":"mk2testmodule","description":"","dist-tags":{"latest":"0.0.1"},"versions":{"0.0.1":{"name":"mk2testmodule","version":"0.0.1","description":"","main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"author":"","license":"ISC","readme":"ERROR: No README data found!","_id":"mk2testmodule@0.0.1","dist":{"shasum":"fa475605f88bab9b1127833633ca3ae0a477224c","tarball":"http://127.0.0.1:7001/mk2testmodule/-/mk2testmodule-0.0.1.tgz"},"_from":".","_npmVersion":"1.4.3","_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}]}},"readme":"ERROR: No README data found!","maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_attachments":{"mk2testmodule-0.0.1.tgz":{"content_type":"application/octet-stream","data":"H4sIAAAAAAAAA+2SsWrDMBCGPfspDg2ZinOyEgeylg6Zu2YR8rVRHEtGkkOg5N0jWaFdujVQAv6W4/7/dHcSGqTq5Ccthxyro7emeDCI2KxWkOKmaaaIdc4TouZQ8FqgwI3AdVMgF8ijho9e5DdGH6SLq/y1T74LfMcn4asEYEb2xLbA+q4O5ENv2/FE7CVZZ3JeW5NcrLDiWW3JK6eHcHey2Es9Zdq0dIkfKau50EcjjYpCmpDKSB0s7Nmbc9ZtwVhIBviBlP7Q1O4ZLBZAFx2As3jyOnWTYzhY9zPzpBUZPy2/e39l5bX87wedmZmZeRJuheTX2wAIAAA=","length":251}}}
|
||||
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 varname = require('modulename');
|
||||
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"
|
||||
}
|
||||
41
test/init.js
Normal file
41
test/init.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/**!
|
||||
* 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',
|
||||
'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;
|
||||
}
|
||||
});
|
||||
});
|
||||
29
test/lib/common.test.js
Normal file
29
test/lib/common.test.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/*!
|
||||
* cnpmjs.org - test/lib/common.test.js.js
|
||||
*
|
||||
* Copyright(c) cnpmjs.org and other contributors.
|
||||
* MIT Licensed
|
||||
*
|
||||
* Authors:
|
||||
* dead_horse <dead_horse@qq.com>
|
||||
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var common = require('../../lib/common');
|
||||
|
||||
describe('lib/common.test.js', function () {
|
||||
describe('isAdmin()', function () {
|
||||
it('should admin is admin', function () {
|
||||
common.isAdmin('admin').should.equal(true);
|
||||
common.isAdmin('fengmk2').should.equal(true);
|
||||
common.isAdmin('constructor').should.equal(false);
|
||||
common.isAdmin('toString').should.equal(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -29,11 +29,12 @@ describe('middleware/auth.test.js', function () {
|
||||
app.close(done);
|
||||
});
|
||||
|
||||
afterEach(mm.restore);
|
||||
|
||||
describe('auth()', function () {
|
||||
afterEach(mm.restore);
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -44,11 +45,12 @@ describe('middleware/auth.test.js', function () {
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it('should 401 with authorization and check fail', function (done) {
|
||||
it('should pass with authorization and check fail', function (done) {
|
||||
// npm install no need to check auth
|
||||
request(app)
|
||||
.get('/-/user/org.couchdb.user:cnpmjstest10')
|
||||
.set('authorization', 'basic ' + new Buffer('cnpmjstest10:cnpmjstest').toString('base64'))
|
||||
.expect(401, done);
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it('should 500 with authorization and mysql error', function (done) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user