diff --git a/.jshintrc b/.jshintrc index da51ea3..6cad781 100644 --- a/.jshintrc +++ b/.jshintrc @@ -24,7 +24,7 @@ // "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 + "unused" : true, // 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 diff --git a/config/index.js b/config/index.js index a7010b0..6e9c1fe 100644 --- a/config/index.js +++ b/config/index.js @@ -156,7 +156,7 @@ var config = { // the storage engine for 'sqlite' storage: path.join(root, 'database.sqlite'), - logging: false, + // logging: false, }, // forward Compat with old style diff --git a/controllers/registry/deprecate.js b/controllers/registry/deprecate.js index fca89eb..544c6a1 100644 --- a/controllers/registry/deprecate.js +++ b/controllers/registry/deprecate.js @@ -14,20 +14,20 @@ * Module dependencies. */ -var Module = require('../../proxy/module'); +var Package = require('../../services/package'); module.exports = deprecateVersions; /** * @see https://github.com/cnpm/cnpmjs.org/issues/415 */ -function* deprecateVersions(next) { +function* deprecateVersions() { var body = this.request.body; var name = this.params.name || this.params[0]; var tasks = []; for (var version in body.versions) { - tasks.push(Module.get(name, version)); + tasks.push(Package.getModule(name, version)); } var rs = yield tasks; @@ -46,12 +46,12 @@ function* deprecateVersions(next) { var data = body.versions[row.package.version]; if (typeof data.deprecated === 'string') { row.package.deprecated = data.deprecated; - updateTasks.push(Module.updatePackage(row.id, row.package)); + updateTasks.push(Package.updateModulePackage(row.id, row.package)); } } yield updateTasks; // update last modified - yield* Module.updateLastModified(name); + yield* Package.updateModuleLastModified(name); this.status = 201; this.body = { diff --git a/controllers/registry/module.js b/controllers/registry/module.js index f1d8c5a..03efe22 100644 --- a/controllers/registry/module.js +++ b/controllers/registry/module.js @@ -16,19 +16,13 @@ */ var debug = require('debug')('cnpmjs.org:controllers:registry:module'); -var path = require('path'); -var fs = require('fs'); var util = require('util'); var crypto = require('crypto'); var utility = require('utility'); -var coRead = require('co-read'); -var coWrite = require('co-write'); var urlparse = require('url').parse; var mime = require('mime'); var semver = require('semver'); -var ms = require('ms'); var config = require('../../config'); -var Module = require('../../proxy/module'); var Total = require('../../services/total'); var nfs = require('../../common/nfs'); var common = require('../../lib/common'); @@ -325,7 +319,7 @@ exports.get = function* (next) { this.body = { error: 'not exist', reason: 'version not found: ' + version - } + }; return; } @@ -338,7 +332,7 @@ exports.download = function *(next) { var name = this.params.name || this.params[0]; var filename = this.params.filename || this.params[1]; var version = filename.slice(name.length + 1, -4); - var row = yield Module.get(name, version); + var row = yield* Package.getModule(name, version); // can not get dist var url = null; @@ -374,7 +368,7 @@ exports.download = function *(next) { _downloads[name] = (_downloads[name] || 0) + 1; - if (typeof dist.size === 'number') { + if (typeof dist.size === 'number' && dist.size > 0) { this.length = dist.size; } this.type = mime.lookup(dist.key); @@ -551,7 +545,7 @@ exports.addPackageAndDist = function *(next) { debug('%s addPackageAndDist %s:%s, attachment size: %s, maintainers: %j, distTags: %j', username, name, version, attachment.length, versionPackage.maintainers, distTags); - var exists = yield Module.get(name, version); + var exists = yield* Module.getModule(name, version); var shasum; if (exists) { this.status = 403; @@ -620,7 +614,7 @@ exports.addPackageAndDist = function *(next) { mod.package.dist = dist; _addDepsRelations(mod.package); - var addResult = yield Module.add(mod); + var addResult = yield* Package.addModule(mod); debug('%s module: save file to %s, size: %d, sha1: %s, dist: %j, version: %s', addResult.id, dist.tarball, dist.size, shasum, dist, version); @@ -818,7 +812,7 @@ exports.removeWithVersions = function* (next) { debug('no tag need to be remove'); } // step 7: update last modified, make sure etag change - yield* Module.updateLastModified(name); + yield* Module.updateModuleLastModified(name); this.status = 201; this.body = { ok: true }; @@ -857,7 +851,7 @@ exports.removeTar = function* (next) { var rs = yield [ Module.getById(id), - Module.get(name, version), + Package.getModule(name, version), ]; var revertTo = rs[0]; var mod = rs[1]; // module need to delete @@ -1034,7 +1028,7 @@ exports.updateTag = function* () { return; } - var mod = yield Module.get(name, version); + var mod = yield* Package.getModule(name, version); if (!mod) { this.status = 403; var reason = util.format('setting tag %s to unknown version: %s: %s/%s', diff --git a/controllers/registry/user_package.js b/controllers/registry/user_package.js index c747286..fced147 100644 --- a/controllers/registry/user_package.js +++ b/controllers/registry/user_package.js @@ -39,7 +39,7 @@ exports.list = function* () { var r = yield [ NpmModuleMaintainer.listByUsers(users), // get the first user module by author field - Module.listNamesByAuthor(firstUser), + Package.listPublicModuleNamesByUser(firstUser), ]; var rows = r[0]; var firstUserModuleNames = r[1]; diff --git a/docs/db.sql b/docs/db.sql index 179a41d..15c4ddf 100644 --- a/docs/db.sql +++ b/docs/db.sql @@ -177,7 +177,7 @@ CREATE TABLE IF NOT EXISTS `dist_dir` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key', `gmt_create` datetime NOT NULL COMMENT 'create time', `gmt_modified` datetime NOT NULL COMMENT 'modified time', - `name` varchar(200) NOT NULL COMMENT 'user name', + `name` varchar(200) NOT NULL COMMENT 'dir name', `parent` varchar(200) NOT NULL COMMENT 'parent dir' DEFAULT '/', `date` varchar(20) COMMENT '02-May-2014 01:06', PRIMARY KEY (`id`), @@ -189,7 +189,7 @@ CREATE TABLE IF NOT EXISTS `dist_file` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key', `gmt_create` datetime NOT NULL COMMENT 'create time', `gmt_modified` datetime NOT NULL COMMENT 'modified time', - `name` varchar(100) NOT NULL COMMENT 'user name', + `name` varchar(100) NOT NULL COMMENT 'file name', `parent` varchar(200) NOT NULL COMMENT 'parent dir' DEFAULT '/', `date` varchar(20) COMMENT '02-May-2014 01:06', `size` int(10) unsigned NOT NULL COMMENT 'file size' DEFAULT '0', diff --git a/models/dist_dir.js b/models/dist_dir.js new file mode 100644 index 0000000..f0be3bd --- /dev/null +++ b/models/dist_dir.js @@ -0,0 +1,64 @@ +/**! + * cnpmjs.org - models/dist_dir.js + * + * Copyright(c) fengmk2 and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 (http://fengmk2.github.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +/* +CREATE TABLE IF NOT EXISTS `dist_dir` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key', + `gmt_create` datetime NOT NULL COMMENT 'create time', + `gmt_modified` datetime NOT NULL COMMENT 'modified time', + `name` varchar(200) NOT NULL COMMENT 'dir name', + `parent` varchar(200) NOT NULL COMMENT 'parent dir' DEFAULT '/', + `date` varchar(20) COMMENT '02-May-2014 01:06', + PRIMARY KEY (`id`), + UNIQUE KEY `dist_dir_parent_name` (`parent`, `name`), + KEY `dist_dir_gmt_modified` (`gmt_modified`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='dist dir info'; + */ + +module.exports = function (sequelize, DataTypes) { + return sequelize.define('DistDir', { + name: { + type: DataTypes.STRING(200), + allowNull: false, + comment: 'dir name', + }, + parent: { + type: DataTypes.STRING(200), + allowNull: false, + defaultValue: '/', + comment: 'parent dir', + }, + date: { + type: DataTypes.STRING(20), + allowNull: false, + comment: '02-May-2014 01:06' + } + }, { + tableName: 'dist_dir', + comment: 'dist dir info', + indexes: [ + { + unique: true, + fields: ['parent', 'name'] + }, + { + fields: ['gmt_modified'] + } + ], + classMethods: { + } + }); +}; diff --git a/models/dist_file.js b/models/dist_file.js new file mode 100644 index 0000000..1a77b03 --- /dev/null +++ b/models/dist_file.js @@ -0,0 +1,82 @@ +/**! + * cnpmjs.org - models/dist_file.js + * + * Copyright(c) fengmk2 and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 (http://fengmk2.github.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +/* +CREATE TABLE IF NOT EXISTS `dist_file` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key', + `gmt_create` datetime NOT NULL COMMENT 'create time', + `gmt_modified` datetime NOT NULL COMMENT 'modified time', + `name` varchar(100) NOT NULL COMMENT 'file name', + `parent` varchar(200) NOT NULL COMMENT 'parent dir' DEFAULT '/', + `date` varchar(20) COMMENT '02-May-2014 01:06', + `size` int(10) unsigned NOT NULL COMMENT 'file size' DEFAULT '0', + `sha1` varchar(40) COMMENT 'sha1 hex value', + `url` varchar(2048), + PRIMARY KEY (`id`), + UNIQUE KEY `dist_file_parent_name` (`parent`, `name`), + KEY `dist_file_gmt_modified` (`gmt_modified`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='dist file info'; + */ + +module.exports = function (sequelize, DataTypes) { + return sequelize.define('DistFile', { + name: { + type: DataTypes.STRING(200), + allowNull: false, + comment: 'dir name', + }, + parent: { + type: DataTypes.STRING(200), + allowNull: false, + defaultValue: '/', + comment: 'parent dir', + }, + date: { + type: DataTypes.STRING(20), + allowNull: false, + comment: '02-May-2014 01:06' + }, + size: { + type: DataTypes.INTEGER(10).UNSIGNED, + allowNull: false, + defaultValue: 0, + comment: 'file size' + }, + sha1: { + type: DataTypes.STRING(40), + allowNull: false, + comment: 'sha1 hex value' + }, + url: { + type: DataTypes.STRING(2048), + allowNull: false + } + }, { + tableName: 'dist_file', + comment: 'dist file info', + indexes: [ + { + unique: true, + fields: ['parent', 'name'] + }, + { + fields: ['gmt_modified'] + } + ], + classMethods: { + } + }); +}; diff --git a/models/index.js b/models/index.js index fcbf98c..807ef90 100644 --- a/models/index.js +++ b/models/index.js @@ -21,9 +21,12 @@ function load(name) { return sequelize.import(path.join(__dirname, name)); } -var models = module.exports = { +module.exports = { sequelize: sequelize, Module: load('module'), + ModuleKeyword: load('module_keyword'), + NpmModuleMaintainer: load('npm_module_maintainer'), + Tag: load('tag'), User: load('user'), Total: load('total'), Download: load('download_total'), diff --git a/models/module.js b/models/module.js index a38ce44..4f4f757 100644 --- a/models/module.js +++ b/models/module.js @@ -14,8 +14,6 @@ * Module dependencies. */ -var utility = require('utility'); - /* CREATE TABLE IF NOT EXISTS `module` ( `id` INTEGER NOT NULL auto_increment , @@ -100,7 +98,11 @@ module.exports = function (sequelize, DataTypes) { } ], classMethods: { - + findByNameAndVersion: function* (name, version) { + return yield this.find({ + where: { name: name, version: version } + }); + } } }); }; diff --git a/models/module_deps.js b/models/module_deps.js new file mode 100644 index 0000000..338175d --- /dev/null +++ b/models/module_deps.js @@ -0,0 +1,55 @@ +/**! + * cnpmjs.org - models/module_deps.js + * + * Copyright(c) fengmk2 and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 (http://fengmk2.github.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +/* +CREATE TABLE IF NOT EXISTS `module_deps` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key', + `gmt_create` datetime NOT NULL COMMENT 'create time', + `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name', + `deps` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'which module depend on this module', + PRIMARY KEY (`id`), + UNIQUE KEY `module_deps_name_deps` (`name`,`deps`), + KEY `name` (`module_deps_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module deps'; + */ + +module.exports = function (sequelize, DataTypes) { + return sequelize.define('ModuleDeps', { + name: { + type: DataTypes.STRING(100), + allowNull: false, + comment: 'module name', + }, + deps: { + type: DataTypes.STRING(100), + comment: 'which module depend on this module' + } + }, { + tableName: 'module_deps', + comment: 'module deps', + indexes: [ + { + unique: true, + fields: ['name', 'deps'] + }, + { + fields: ['name'] + } + ], + classMethods: { + } + }); +}; diff --git a/models/module_keyword.js b/models/module_keyword.js new file mode 100644 index 0000000..0ce3362 --- /dev/null +++ b/models/module_keyword.js @@ -0,0 +1,68 @@ +/**! + * cnpmjs.org - models/module_keyword.js + * + * Copyright(c) fengmk2 and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 (http://fengmk2.github.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +/* +CREATE TABLE IF NOT EXISTS `module_keyword` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key', + `gmt_create` datetime NOT NULL COMMENT 'create time', + `keyword` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'keyword', + `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name', + `description` longtext, + PRIMARY KEY (`id`), + UNIQUE KEY `keyword_module_name` (`keyword`,`name`), + KEY `name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module keyword'; + */ + +module.exports = function (sequelize, DataTypes) { + return sequelize.define('ModuleKeyword', { + keyword: { + type: DataTypes.STRING(100), + allowNull: false, + }, + name: { + type: DataTypes.STRING(100), + allowNull: false, + comment: 'module name', + }, + description: { + type: DataTypes.LONGTEXT, + allowNull: true, + } + }, { + tableName: 'module_keyword', + comment: 'module keyword', + indexes: [ + { + unique: true, + fields: ['keyword', 'name'] + }, + { + fields: ['name'] + } + ], + classMethods: { + findByKeywordAndName: function* (keyword, name) { + return yield this.find({ + where: { + keyword: keyword, + name: name + } + }); + } + } + }); +}; diff --git a/models/module_log.js b/models/module_log.js new file mode 100644 index 0000000..37a8c9e --- /dev/null +++ b/models/module_log.js @@ -0,0 +1,56 @@ +/**! + * cnpmjs.org - models/module_log.js + * + * Copyright(c) fengmk2 and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 (http://fengmk2.github.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +/* +CREATE TABLE IF NOT EXISTS `module_log` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key', + `gmt_create` datetime NOT NULL COMMENT 'create time', + `gmt_modified` datetime NOT NULL COMMENT 'modified time', + `username` varchar(100) NOT NULL, + `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name', + `log` longtext, + PRIMARY KEY (`id`), + KEY `module_log_name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module sync log'; + */ + +module.exports = function (sequelize, DataTypes) { + return sequelize.define('ModuleLog', { + username: { + type: DataTypes.STRING(100), + allowNull: false, + comment: 'user name' + }, + name: { + type: DataTypes.STRING(100), + allowNull: false, + comment: 'module name', + }, + log: { + type: DataTypes.LONGTEXT + } + }, { + tableName: 'module_log', + comment: 'module sync log', + indexes: [ + { + fields: ['name'] + } + ], + classMethods: { + } + }); +}; diff --git a/models/module_maintainer.js b/models/module_maintainer.js new file mode 100644 index 0000000..0d52c65 --- /dev/null +++ b/models/module_maintainer.js @@ -0,0 +1,56 @@ +/**! + * cnpmjs.org - models/module_maintainer.js + * + * Copyright(c) fengmk2 and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 (http://fengmk2.github.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +/* +CREATE TABLE IF NOT EXISTS `module_maintainer` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key', + `gmt_create` datetime NOT NULL COMMENT 'create time', + `user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name', + `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name', + PRIMARY KEY (`id`), + UNIQUE KEY `module_maintainer_user_name` (`user`,`name`), + KEY `name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='private module maintainers'; + */ + +module.exports = function (sequelize, DataTypes) { + return sequelize.define('ModuleMaintainer', { + user: { + type: DataTypes.STRING(100), + allowNull: false, + comment: 'user name' + }, + name: { + type: DataTypes.STRING(100), + allowNull: false, + comment: 'module name', + } + }, { + tableName: 'module_maintainer', + comment: 'private module maintainers', + indexes: [ + { + unique: true, + fields: ['user', 'name'] + }, + { + fields: ['name'] + } + ], + classMethods: { + } + }); +}; diff --git a/models/module_star.js b/models/module_star.js new file mode 100644 index 0000000..1831287 --- /dev/null +++ b/models/module_star.js @@ -0,0 +1,56 @@ +/**! + * cnpmjs.org - models/module_star.js + * + * Copyright(c) fengmk2 and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 (http://fengmk2.github.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +/* +CREATE TABLE IF NOT EXISTS `module_star` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key', + `gmt_create` datetime NOT NULL COMMENT 'create time', + `user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name', + `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name', + PRIMARY KEY (`id`), + UNIQUE KEY `user_module_name` (`user`,`name`), + KEY `name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module star'; + */ + +module.exports = function (sequelize, DataTypes) { + return sequelize.define('ModuleStar', { + user: { + type: DataTypes.STRING(100), + allowNull: false, + comment: 'user name' + }, + name: { + type: DataTypes.STRING(100), + allowNull: false, + comment: 'module name', + } + }, { + tableName: 'module_star', + comment: 'module star', + indexes: [ + { + unique: true, + fields: ['user', 'name'] + }, + { + fields: ['name'] + } + ], + classMethods: { + } + }); +}; diff --git a/models/module_unpublished.js b/models/module_unpublished.js new file mode 100644 index 0000000..6833442 --- /dev/null +++ b/models/module_unpublished.js @@ -0,0 +1,56 @@ +/**! + * cnpmjs.org - models/module_unpublished.js + * + * Copyright(c) fengmk2 and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 (http://fengmk2.github.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +/* +CREATE TABLE IF NOT EXISTS `module_unpublished` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key', + `gmt_create` datetime NOT NULL COMMENT 'create time', + `gmt_modified` datetime NOT NULL COMMENT 'modified time', + `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name', + `package` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'base info: tags, time, maintainers, description, versions', + PRIMARY KEY (`id`), + UNIQUE KEY `module_unpublished_name` (`name`), + KEY `module_unpublished_gmt_modified` (`gmt_modified`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module unpublished info'; + */ + +module.exports = function (sequelize, DataTypes) { + return sequelize.define('ModuleUnpublished', { + name: { + type: DataTypes.STRING(100), + allowNull: false, + comment: 'module name', + }, + package: { + type: DataTypes.LONGTEXT, + comment: 'base info: tags, time, maintainers, description, versions' + } + }, { + tableName: 'module_unpublished', + comment: 'module unpublished info', + indexes: [ + { + unique: true, + fields: ['name'] + }, + { + fields: ['gmt_modified'] + } + ], + classMethods: { + } + }); +}; diff --git a/models/npm_module_maintainer.js b/models/npm_module_maintainer.js new file mode 100644 index 0000000..9f57a42 --- /dev/null +++ b/models/npm_module_maintainer.js @@ -0,0 +1,63 @@ +/**! + * cnpmjs.org - models/npm_module_maintainer.js + * + * Copyright(c) fengmk2 and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 (http://fengmk2.github.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +/* +CREATE TABLE IF NOT EXISTS `npm_module_maintainer` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key', + `gmt_create` datetime NOT NULL COMMENT 'create time', + `user` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'user name', + `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name', + PRIMARY KEY (`id`), + UNIQUE KEY `npm_module_maintainer_user_name` (`user`,`name`), + KEY `name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='npm original module maintainers'; + */ + +module.exports = function (sequelize, DataTypes) { + return sequelize.define('NpmModuleMaintainer', { + user: { + type: DataTypes.STRING(100), + allowNull: false, + comment: 'user name' + }, + name: { + type: DataTypes.STRING(100), + allowNull: false, + comment: 'module name', + } + }, { + tableName: 'npm_module_maintainer', + comment: 'npm original module maintainers', + indexes: [ + { + unique: true, + fields: ['user', 'name'] + }, + { + fields: ['name'] + } + ], + classMethods: { + listByUser: function* (user) { + return yield this.findAll({ + where: { + user: user + } + }); + }, + } + }); +}; diff --git a/models/tag.js b/models/tag.js new file mode 100644 index 0000000..dc0617a --- /dev/null +++ b/models/tag.js @@ -0,0 +1,72 @@ +/**! + * cnpmjs.org - models/tag.js + * + * Copyright(c) fengmk2 and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 (http://fengmk2.github.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +/* +CREATE TABLE IF NOT EXISTS `tag` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key', + `gmt_create` datetime NOT NULL COMMENT 'create time', + `gmt_modified` datetime NOT NULL COMMENT 'modified time', + `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'module name', + `tag` varchar(30) NOT NULL COMMENT 'tag name', + `version` varchar(30) NOT NULL COMMENT 'module version', + `module_id` bigint(20) unsigned NOT NULL COMMENT 'module id', + PRIMARY KEY (`id`), + UNIQUE KEY `tag_name_tag` (`name`, `tag`), + KEY `tag_gmt_modified` (`gmt_modified`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module tag'; + */ + +module.exports = function (sequelize, DataTypes) { + return sequelize.define('Tag', { + name: { + type: DataTypes.STRING(100), + allowNull: false, + comment: 'module name', + }, + tag: { + type: DataTypes.STRING(30), + allowNull: false, + comment: 'tag name', + }, + version: { + type: DataTypes.STRING(30), + allowNull: false, + comment: 'module version', + }, + module_id: { + type: DataTypes.BIGINT(20).UNSIGNED, + allowNull: false, + comment: 'module id' + } + }, { + tableName: 'tag', + comment: 'module tag', + indexes: [ + { + unique: true, + fields: ['name', 'tag'] + }, + { + fields: ['gmt_modified'] + } + ], + classMethods: { + findByNameAndTag: function* (name, tag) { + return yield this.find({ where: { name: name, tag: tag } }); + } + } + }); +}; diff --git a/models/total.js b/models/total.js index ddc4722..8e7100d 100644 --- a/models/total.js +++ b/models/total.js @@ -14,8 +14,6 @@ * Module dependencies. */ -var utility = require('utility'); - // CREATE TABLE IF NOT EXISTS `total` ( // `name` varchar(100) NOT NULL COMMENT 'total name', // `gmt_modified` datetime NOT NULL COMMENT 'modified time', diff --git a/models/user.js b/models/user.js index 8c07fdc..700705f 100644 --- a/models/user.js +++ b/models/user.js @@ -16,6 +16,29 @@ var utility = require('utility'); +/* +CREATE TABLE IF NOT EXISTS `user` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key', + `gmt_create` datetime NOT NULL COMMENT 'create time', + `gmt_modified` datetime NOT NULL COMMENT 'modified time', + `name` varchar(100) NOT NULL COMMENT 'user name', + `salt` varchar(100) NOT NULL, + `password_sha` varchar(100) NOT NULL COMMENT 'user password hash', + `ip` varchar(64) NOT NULL COMMENT 'user last request ip', + `roles` varchar(200) NOT NULL DEFAULT '[]', + `rev` varchar(40) NOT NULL, + `email` varchar(400) NOT NULL, + `json` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'json details', + `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'; + */ + module.exports = function (sequelize, DataTypes) { return sequelize.define('User', { name: { diff --git a/proxy/module.js b/proxy/module.js deleted file mode 100644 index 2b45c15..0000000 --- a/proxy/module.js +++ /dev/null @@ -1,778 +0,0 @@ -/**! - * cnpmjs.org - proxy/module.js - * - * Copyright(c) cnpmjs.org and other contributors. - * MIT Licensed - * - * Authors: - * fengmk2 (http://fengmk2.github.com) - */ - -'use strict'; -/* jshint -W032 */ - -/** - * 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 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); - } catch (e) { - return callback(e); - } - var description = mod.package && mod.package.description || ''; - - var dist = mod.package.dist || {}; - dist.tarball = ''; - dist.shasum = ''; - dist.size = 0; - var publish_time = mod.publish_time || Date.now(); - var values = [ - publish_time, - mod.author, // meaning first maintainer, more maintainers please check module_maintainer table - mod.name, mod.version, pkg, - dist.tarball, dist.shasum, dist.size, description - ]; - mysql.query(INSERT_MODULE_SQL, values, function (err, result) { - if (err) { - 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 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 ? WHERE id=?'; -exports.update = function (mod, callback) { - var pkg; - try { - pkg = stringifyPackage(mod.package); - } catch (e) { - return callback(e); - } - var dist = mod.package.dist; - - var arg = { - publish_time: mod.publish_time, - version: mod.version, - package: pkg, - dist_tarball: dist.tarball, - dist_shasum: dist.shasum, - dist_size: dist.size - }; - - mysql.query(UPDATE_DIST_SQL, - [arg, mod.id], - function (err, result) { - if (err) { - return callback(err); - } - callback(null, {id: mod.id, gmt_modified: new Date()}); - }); -}; - -function parseRow(row) { - if (row && row.package) { - try { - if (row.package.indexOf('%7B%22') === 0) { - // now store package will encodeURIComponent() after JSON.stringify - row.package = decodeURIComponent(row.package); - } - row.package = JSON.parse(row.package); - } catch (e) { - console.warn('parse package error: %s, id: %s version: %s, error: %s', row.name, row.id, row.version, e); - } - } -} -exports.parseRow = parseRow; - -function stringifyPackage(pkg) { - return encodeURIComponent(JSON.stringify(pkg)); -} - - -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) { - if (err || !row) { - return callback(err, row); - } - try { - parseRow(row); - } catch (e) { - e.data = row; - return callback(e); - } - callback(null, row); - }); -}; - -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) { - return callback(err, row); - } - try { - parseRow(row); - } catch (e) { - e.data = row; - return callback(e); - } - callback(null, row); - }); -}; - -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) { - return callback(err); - } - var module_id = row && row.id || 0; - mysql.query(INSERT_TAG_SQL, [name, tag, version, module_id], function (err, result) { - if (err) { - return callback(err); - } - callback(null, {id: result.insertId, gmt_modified: new Date(), module_id: module_id}); - }); - }); -}; - -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) { - return callback(err, row); - } - exports.get(name, row.version, callback); - }); -}; - -var DELETE_TAGS_SQL = multiline(function () {;/* - DELETE FROM - tag - WHERE - name=?; -*/}); -exports.removeTags = function (name, callback) { - mysql.query(DELETE_TAGS_SQL, [name], callback); -}; - -var DELETE_TAGS_BY_IDS_SQL = 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 = 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) { - return callback(err, row); - } - - // get latest order by id - mysql.queryOne(SELECT_LATEST_MODULE_SQL, [name], function (err, row) { - if (err || !row) { - return callback(err, row); - } - try { - parseRow(row); - } catch (e) { - e.data = row; - return callback(e); - } - callback(null, row); - }); - }); -}; - -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) { - return callback(err); - } - - rows = rows || []; - try { - for (var i = 0; i < rows.length; i++) { - parseRow(rows[i]); - } - } catch (e) { - err = e; - err.data = rows; - } - callback(err, rows); - }); -}; - -var LIST_SINCE_SQL = multiline(function () {;/* - SELECT - distinct(name) - FROM - tag - WHERE - gmt_modified > ?; -*/}); -exports.listSince = function (start, callback) { - mysql.query(LIST_SINCE_SQL, [new Date(start)], callback); -}; - -var LIST_ALL_NAME_SQL = multiline(function () {;/* - SELECT - distinct(name) - FROM - module; -*/}); -exports.listAllNames = function (callback) { - mysql.query(LIST_ALL_NAME_SQL, [], callback); -}; - -var LIST_SHORT_SQL = multiline(function () {;/* - SELECT - distinct(name) - FROM - tag - ORDER BY - name; -*/}); -exports.listShort = function (callback) { - mysql.query(LIST_SHORT_SQL, callback); -}; - -var LIST_ALL_MODULE_NAMES_SQL = 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 = 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 SEARCH_MODULES_SQL = multiline(function () {;/* - SELECT - module_id - FROM - tag - WHERE - LOWER(name) LIKE LOWER(?) 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); - - // 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; - } - } - if (rows.length >= 20) { - return ep.emit('ids', Object.keys(ids)); - } - - mysql.query(SEARCH_MODULES_SQL, [ '%' + word + '%', limit ], ep.done('likeSearch')); - })); - - 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]); -}; - -/** - * forward compatbility for update from lower version cnpmjs.org - * redirect @scope/name => name - */ -exports.getAdaptName = function* (name) { - if (!config.scopes - || !config.scopes.length - || !config.adaptScope) { - return; - } - - var tmp = name.split('/'); - var scope = tmp[0]; - name = tmp[1]; - - if (config.scopes.indexOf(scope) === -1) { - return; - } - - var pkg = yield exports.getByTag(name, 'latest'); - // only private module can adapt - if (pkg && pkg.package._publish_on_cnpm) { - return name; - } - return; -}; - -exports.listPrivates = function* () { - var scopes = config.scopes; - if (!scopes || !scopes.length) { - return []; - } - var privatePackages = config.privatePackages || []; - - var args = []; - var sql = 'SELECT module_id AS id FROM tag WHERE tag="latest" AND ('; - var wheres = []; - - scopes.forEach(function (scope) { - wheres.push('name LIKE ?'); - args.push(scope + '%'); - }); - - if (privatePackages.length) { - wheres.push('name in (?)'); - args.push(privatePackages); - } - - sql = sql + wheres.join(' OR ') + ')'; - - var ids = yield mysql.query(sql, args); - ids = ids.map(function (row) { - return row.id; - }); - - if (!ids.length) { - return []; - } - - return yield mysql.query(QUERY_MODULES_BY_ID_SQL, [ids]); -}; - -var LIST_BY_AUTH_SQLS = []; -LIST_BY_AUTH_SQLS.push(multiline(function () {;/* - SELECT - distinct(name) AS name - FROM - module - WHERE - author=? - ORDER BY - publish_time DESC - LIMIT - 100; -*/})); -LIST_BY_AUTH_SQLS.push(multiline(function () {;/* - SELECT - name - FROM - module_maintainer - WHERE - user = ? -*/})); -LIST_BY_AUTH_SQLS.push(multiline(function () {;/* - SELECT - module_id - FROM - tag - WHERE - tag="latest" AND name IN (?); -*/})); -LIST_BY_AUTH_SQLS.push(multiline(function () {;/* - SELECT - name, description - FROM - module - WHERE - id IN (?) - ORDER BY - publish_time DESC; -*/})); -exports.listByAuthor = function* (author) { - var names = yield [ - mysql.query(LIST_BY_AUTH_SQLS[0], [author]), - mysql.query(LIST_BY_AUTH_SQLS[1], [author]) - ]; - - names = names[0].concat(names[1]).map(function (n) { - return n.name; - }).sort(); - - if (!names.length) { - return []; - } - - var ids = yield mysql.query(LIST_BY_AUTH_SQLS[2], [names]); - if (!ids.length) { - return []; - } - - ids = ids.map(function (i) { - return i.module_id; - }); - return yield mysql.query(LIST_BY_AUTH_SQLS[3], [ids]); -}; - -exports.listNamesByAuthor = function* (author) { - var sql = 'SELECT distinct(name) AS name FROM module WHERE author=?;'; - var names = yield mysql.query(sql, [author]); - return names.map(function (item) { - return item.name; - }); -}; - -var UPDATE_PACKAGE_SQL = multiline(function () {;/* - UPDATE - module - SET - package=? - WHERE - id=?; -*/}); - -exports.updatePackage = function* (id, pkg) { - pkg = stringifyPackage(pkg); - return yield mysql.query(UPDATE_PACKAGE_SQL, [pkg, id]); -}; - -exports.updatePackageFields = function* (id, fields) { - var data = yield exports.getById(id); - if (!data) { - throw new Error('module#' + id + ' not exists'); - } - data.package = data.package || {}; - for (var k in fields) { - data.package[k] = fields[k]; - } - return yield* exports.updatePackage(id, data.package); -}; - -exports.updateReadme = function* (id, readme) { - var data = yield exports.getById(id); - if (!data) { - throw new Error('module#' + id + ' not exists'); - } - data.package = data.package || {}; - data.package.readme = readme; - return yield* exports.updatePackage(id, data.package); -}; - -exports.getTag = function* (name, tag) { - return yield mysql.queryOne(SELECT_TAG_SQL, [name, tag]); -}; diff --git a/proxy/sync_module_worker.js b/proxy/sync_module_worker.js index f410e80..36271ec 100644 --- a/proxy/sync_module_worker.js +++ b/proxy/sync_module_worker.js @@ -28,7 +28,6 @@ var crypto = require('crypto'); var sleep = require('co-sleep'); var urllib = require('../common/urllib'); var utility = require('utility'); -var ms = require('ms'); var urlparse = require('url').parse; var nfs = require('../common/nfs'); var npm = require('./npm'); @@ -522,7 +521,7 @@ SyncModuleWorker.prototype._sync = function* (name, pkg) { }, }; try { - var result = yield Module.add(nextMod); + var result = yield* Package.addModule(nextMod); that.log(' [%s] save next module, %j', name, result); } catch (err) { that.log(' [%s] save next module error %s', err.message); @@ -1002,7 +1001,7 @@ SyncModuleWorker.prototype._syncOneVersion = function *(versionIndex, sourcePack } mod.package.dist = dist; - var r = yield Module.add(mod); + var r = yield* Package.addModule(mod); that.log(' [%s:%s] done, insertId: %s, author: %s, version: %s, ' + 'size: %d, publish_time: %j, publish on cnpm: %s', diff --git a/servers/registry.js b/servers/registry.js index aa8b819..3234659 100644 --- a/servers/registry.js +++ b/servers/registry.js @@ -56,6 +56,7 @@ routes(app); app.on('error', function (err, ctx) { console.log(err); + console.log(err.stack); err.url = err.url || ctx.request.url; logger.error(err); }); diff --git a/services/package.js b/services/package.js index 4d71ea1..69eda5c 100644 --- a/services/package.js +++ b/services/package.js @@ -14,9 +14,368 @@ * Module dependencies. */ -var Module = require('../proxy/module'); -var ModuleMaintainer = require('../proxy/module_maintainer'); var User = require('../proxy/user'); +var models = require('../models'); +var Module = models.Module; +var ModuleKeyword = models.ModuleKeyword; +var NpmModuleMaintainer = models.NpmModuleMaintainer; +var ModuleMaintainer = models.ModuleMaintainer; +var Tag = models.Tag; + +// module + +// module:read +function parseRow(row) { + if (row && row.package) { + try { + if (row.package.indexOf('%7B%22') === 0) { + // now store package will encodeURIComponent() after JSON.stringify + row.package = decodeURIComponent(row.package); + } + row.package = JSON.parse(row.package); + } catch (e) { + console.warn('parse package error: %s, id: %s version: %s, error: %s', row.name, row.id, row.version, e); + } + } +} +exports.parseRow = parseRow; + +function stringifyPackage(pkg) { + return encodeURIComponent(JSON.stringify(pkg)); +} + +exports.getModuleById = function* (id) { + var row = yield Module.find(Number(id)); + parseRow(row); + return row; +}; + +exports.getModule = function* (name, version) { + var row = yield* Module.findByNameAndVersion(name, version); + parseRow(row); + return row; +}; + +exports.getModuleByTag = function* (name, tag) { + var tag = yield* Tag.findByNameAndTag(name, tag); + if (!tag) { + return null; + } + return yield* exports.getModule(tag.name, tag.version); +}; + +// module:list + +exports.listPrivateModulesByScope = function* (scope) { + var tags = yield Tag.findAll({ + where: { + tag: 'latest', + name: { + like: scope + '%' + } + } + }); + + if (tags.length === 0) { + return []; + } + + var ids = tags.map(function (tag) { + return tag.module_id; + }); + + return yield Module.findAll({ + where: { + id: ids + } + }); +}; + +exports.listPublicModulesByUser = function* (username) { + var names = yield* exports.listPublicModuleNamesByUser(username); + if (names.length === 0) { + return []; + } + + // fetch latest module tags + var tags = yield Tag.findAll({ + where: { + name: names, + tag: 'latest' + } + }); + if (tags.length === 0) { + return []; + } + + var ids = tags.map(function (tag) { + return tag.module_id; + }); + return yield Module.findAll({ + where: { + id: ids + }, + attributes: [ + 'name', 'description' + ] + }); +}; + +// return user all public package names +exports.listPublicModuleNamesByUser = function* (username) { + var sql = 'SELECT distinct(name) AS name FROM module WHERE author=?;'; + var rows = yield* models.query(sql, [username]); + var map = {}; + var names = rows.map(function (r) { + return r.name; + }).filter(function (name) { + var matched = name[0] !== '@'; + if (matched) { + map[name] = 1; + } + return matched; + }); + + // find from npm module maintainer table + var items = yield* NpmModuleMaintainer.listByUser(username); + items.forEach(function (item) { + if (!map[item.name]) { + names.push(item.name); + } + }); + return names; +}; + +// start must be a date or timestamp +exports.listPublicModuleNamesSince = function* (start) { + if (!(start instanceof Date)) { + start = new Date(Number(start)); + } + var rows = yield Tag.findAll({ + attributes: ['name'], + where: { + gmt_modified: { + gt: start + } + }, + }); + var names = {}; + for (var i = 0; i < rows.length; i++) { + names[rows[i].name] = 1; + } + return Object.keys(names); +}; + +exports.listAllPublicModuleNames = function* () { + var sql = 'SELECT DISTINCT(name) AS name FROM tag ORDER BY name'; + var rows = yield models.query(sql); + return rows.filter(function (row) { + return row.name[0] !== '@'; + }).map(function (row) { + return row.name; + }); +}; + +exports.listModulesByName = function* (moduleName) { + var mods = yield Module.findAll({ + where: { + name: moduleName + }, + order: [ ['id', 'DESC'] ] + }); + return mods.map(function (mod) { + parseRow(mod); + return mod; + }); +}; + +exports.getModuleLastModified = function* (name) { + var sql = 'SELECT gmt_modified FROM module WHERE name = ? ORDER BY gmt_modified DESC LIMIT 1'; + var row = yield models.queryOne(sql, [name]); + return row && row.gmt_modified || null; +}; + +// module:update +exports.saveModule = function* (mod) { + var keywords = mod.package.keywords; + if (typeof keywords === 'string') { + keywords = [keywords]; + } + var pkg = stringifyPackage(mod.package); + var description = mod.package && mod.package.description || ''; + var dist = mod.package.dist || {}; + // dist.tarball = ''; + // dist.shasum = ''; + // dist.size = 0; + var publish_time = mod.publish_time || Date.now(); + var item = yield* Module.findByNameAndVersion(mod.name, mod.version); + if (!item) { + item = Module.build({ + name: mod.name, + version: mod.version + }); + } + item.publish_time = publish_time; + // meaning first maintainer, more maintainers please check module_maintainer table + item.author = mod.author; + item.package = pkg; + item.dist_tarball = dist.tarball; + item.dist_shasum = dist.shasum; + item.dist_size = dist.size; + item.description = description; + + var newItem = yield item.save(); + var result = { + id: newItem.id, + gmt_modified: newItem.gmt_modified + }; + + if (!Array.isArray(keywords)) { + return result; + } + + 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) { + // add keywords + yield* exports.addKeywords(mod.name, description, words); + } + + return result; +}; + +exports.updateModulePackage = function* (id, pkg) { + var mod = yield Module.find(Number(id)); + if (!mod) { + // not exists + return null; + } + mod.package = stringifyPackage(pkg); + return yield mod.save(['package']); +}; + +exports.updateModulePackageFields = function* (id, fields) { + var mod = yield* exports.getModuleById(id); + if (!mod) { + return null; + } + var pkg = mod.package || {}; + for (var k in fields) { + pkg[k] = fields[k]; + } + return yield* exports.updateModulePackage(id, pkg); +}; + +exports.updateModuleReadme = function* (id, readme) { + var mod = yield* exports.getModuleById(id); + if (!mod) { + return null; + } + var pkg = mod.package || {}; + pkg.readme = readme; + return yield* exports.updateModulePackage(id, pkg); +}; + +exports.updateModuleDescription = function* (id, description) { + var mod = yield* exports.getModuleById(id); + if (!mod) { + return null; + } + mod.description = description; + // also need to update package.description + var pkg = mod.package || {}; + pkg.description = description; + mod.package = stringifyPackage(pkg); + + return yield mod.save(['description', 'package']); +}; + +exports.updateModuleLastModified = function* (name) { + var row = yield Module.find({ + where: { name: name }, + order: [ [ 'gmt_modified', 'DESC' ] ], + }); + if (!row) { + return null; + } + row.gmt_modified = new Date(); + return yield row.save(['gmt_modified']); +}; + +exports.removeModulesByName = function* (name) { + yield Module.destroy({ + where: { + name: name + } + }); +}; + +exports.removeModulesByNameAndVersions = function* (name, versions) { + yield Module.destroy({ + where: { + name: name, + version: versions + } + }); +}; + +// tags + +exports.addModuleTag = function* (name, tag, version) { + var mod = yield* exports.getModule(name, version); + if (!mod) { + return null; + } + + var row = yield* Tag.findByNameAndTag(name, tag); + if (!row) { + row = Tag.build({ + name: name, + tag: tag + }); + } + row.module_id = mod.id; + row.version = version; + yield row.save(); + + return {id: row.id, gmt_modified: row.gmt_modified, module_id: row.module_id}; +}; + +exports.getModuleTag = function* (name, tag) { + return yield Tag.findByNameAndTag(name, tag); +}; + +exports.removeModuleTagsByName = function* (name) { + return yield Tag.destroy({where: {name: name}}); +}; + +exports.removeModuleTagsByIds = function* (ids) { + return yield Tag.destroy({where: {id: ids}}); +}; + +exports.removeModuleTagsByNames = function* (moduleName, tagNames) { + return yield Tag.destroy({ + where: { + name: moduleName, + tag: tagNames + } + }); +}; + +exports.listModuleTags = function* (name) { + return yield Tag.findAll({ where: { name: name } }); +}; + +// maintainers exports.listMaintainers = function* (name) { var names = yield* ModuleMaintainer.get(name); @@ -43,7 +402,7 @@ exports.addMaintainers = function* (name, usernames) { exports.updateMaintainers = function* (name, usernames) { var rs = yield [ ModuleMaintainer.update(name, usernames), - Module.updateLastModified(name), + exports.updateModuleLastModified(name), ]; return rs[0]; }; @@ -92,3 +451,81 @@ exports.isMaintainer = function* (name, username) { var result = yield* exports.authMaintainer(name, username); return result.isMaintainer; }; + +// module keywords + +exports.addKeyword = function* (data) { + var item = yield ModuleKeyword.findByKeywordAndName(data.keyword, data.name); + if (!item) { + item = ModuleKeyword.build(data); + } + item.description = data.description; + return yield item.save(); +}; + +exports.addKeywords = function* (name, description, keywords) { + var tasks = []; + keywords.forEach(function (keyword) { + tasks.push(exports.addKeyword({ + name: name, + keyword: keyword, + description: description + })); + }); + return yield tasks; +}; + +// search + +exports.search = function* (word, options) { + options = options || {}; + var limit = options.limit || 100; + word = word.replace(/^%/, ''); //ignore prefix % + + // search flows: + // 1. prefix search by name + // 2. like search by name + // 3. keyword equal search + var ids = {}; + + var sql = 'SELECT module_id FROM tag WHERE LOWER(name) LIKE LOWER(?) AND tag="latest" \ + ORDER BY name LIMIT ?;'; + var rows = yield* models.query(sql, [word + '%', limit ]); + for (var i = 0; i < rows.length; i++) { + ids[rows[i].module_id] = 1; + } + + if (rows.length < 20) { + rows = yield* models.query(sql, [ '%' + word + '%', limit ]); + for (var i = 0; i < rows.length; i++) { + ids[rows[i].module_id] = 1; + } + } + + var keywordRows = yield ModuleKeyword.findAll({ + attributes: [ 'name', 'description' ], + where: { + keyword: word + }, + limit: limit, + order: [ [ 'id', 'DESC' ] ] + }); + + var data = { + keywordMatchs: keywordRows, + searchMatchs: [] + }; + + ids = Object.keys(ids); + if (ids.length > 0) { + data.searchMatchs = yield Module.findAll({ + attributes: [ 'name', 'description' ], + where: { + id: ids + }, + order: 'name' + }); + } + + return data; +}; diff --git a/test/controllers/registry/deprecate.test.js b/test/controllers/registry/deprecate.test.js index 15b8255..d248b96 100644 --- a/test/controllers/registry/deprecate.test.js +++ b/test/controllers/registry/deprecate.test.js @@ -14,8 +14,6 @@ * Module dependencies. */ -var fs = require('fs'); -var path = require('path'); var should = require('should'); var request = require('supertest'); var mm = require('mm'); @@ -23,9 +21,7 @@ var pedding = require('pedding'); var app = require('../../../servers/registry'); var utils = require('../../utils'); -var fixtures = path.join(path.dirname(path.dirname(__dirname)), 'fixtures'); - -describe('controllers/registry/deprecate.test.js', function () { +describe.only('controllers/registry/deprecate.test.js', function () { var pkgname = 'testmodule-deprecate'; before(function (done) { done = pedding(2, done); diff --git a/test/proxy/module.test.js b/test/proxy/module.test.js deleted file mode 100644 index 13aaeee..0000000 --- a/test/proxy/module.test.js +++ /dev/null @@ -1,209 +0,0 @@ -/**! - * cnpmjs.org - test/proxy/module.test.js - * - * Copyright(c) cnpmjs.org and other contributors. - * MIT Licensed - * - * Authors: - * fengmk2 (http://fengmk2.github.com) - */ - -'use strict'; - -/** - * Module dependencies. - */ - -var should = require('should'); -var mm = require('mm'); -var fs = require('fs'); -var path = require('path'); -var request = require('supertest'); -var mysql = require('../../common/mysql'); -var Module = require('../../proxy/module'); -var config = require('../../config'); -var utils = require('../utils'); -var app = require('../../servers/registry'); - -var fixtures = path.join(path.dirname(__dirname), 'fixtures'); - -var id; -var pkgname = 'module-proxy-test-pkg'; -var pkgversion = '1.0.0'; - -describe('proxy/module.test.js', function () { - - before(function (done) { - var pkg = utils.getPackage(pkgname, pkgversion, utils.admin); - request(app.listen()) - .put('/' + pkgname) - .set('authorization', utils.adminAuth) - .send(pkg) - .expect(201, done); - }); - - afterEach(mm.restore); - - describe('addTag()', function () { - it('should add tag auto add module id', function (done) { - Module.addTag('mocha', 'test', '1.15.1', function (err, result) { - should.not.exist(err); - result.should.have.keys('id', 'gmt_modified', 'module_id'); - done(); - }); - }); - }); - - describe('search()', function () { - before(function (done) { - Module.addKeywords('aaaa', 'mock aaaaaa', ['aa', 'bb', 'cc'], function (err, results) { - should.not.exist(err); - results.should.be.an.Array; - results.should.length(3); - done(); - }); - }); - - it.skip('should search modules', function (done) { - Module.search('mock', function (err, data) { - should.not.exist(err); - data.should.have.keys('keywordMatchs', 'searchMatchs'); - data.searchMatchs.length.should.above(0); - data.searchMatchs.forEach(function (row) { - row.should.have.keys('name', 'description'); - }); - done(); - }); - }); - - it('should search match keywords modules', function (done) { - Module.search('aa', function (err, data) { - should.not.exist(err); - data.should.have.keys('keywordMatchs', 'searchMatchs'); - data.keywordMatchs.length.should.above(0); - data.keywordMatchs.forEach(function (row) { - row.should.have.keys('name', 'description'); - }); - done(); - }); - }); - - it('should search return empty', function (done) { - Module.search('emptyemptyemptyempty', function (err, data) { - should.not.exist(err); - data.should.eql({ - keywordMatchs: [], - searchMatchs: [] - }); - done(); - }); - }); - }); - - var mockName = 'aa' + Date.now(); - describe('addKeywords()', function () { - it('should add diff keywords to module', function (done) { - Module.addKeywords(mockName, mockName, ['aa', 'bb', 'cc'], function (err, results) { - should.not.exist(err); - results.should.be.an.Array; - results.should.length(3); - done(); - }); - }); - - it('should add same keywords to module', function (done) { - Module.addKeywords(mockName, 'desc aa', ['aa', 'bb', 'cc'], function (err, results) { - should.not.exist(err); - results.should.be.an.Array; - results.should.length(3); - // results.should.length(0); - done(); - }); - }); - }); - - describe('getKeywords()', function () { - it('should get aa module keywords', function (done) { - Module.getKeywords(mockName, function (err, keywords) { - should.not.exist(err); - keywords.should.eql(['aa', 'bb', 'cc']); - done(); - }); - }); - }); - - describe('add()', function () { - it('should success ad he@0.3.6', function (done) { - var sourcePackage = require('../fixtures/0.3.6.json'); - var mod = { - version: sourcePackage.version, - name: sourcePackage.name, - package: sourcePackage, - author: 'unittest', - publish_time: sourcePackage.publish_time || Date.now(), - }; - var dist = { - tarball: 'http://registry.npmjs.org/he/-/he-0.3.6.tgz', - shasum: '9d7bc446e77963933301dd602d5731cb861135e0', - size: 100, - }; - mod.package.dist = dist; - Module.add(mod, function (err, result) { - id = result.id; - should.not.exist(err); - Module.getById(result.id, function (err, row) { - should.not.exist(err); - row.package.readme.should.equal(sourcePackage.readme); - done(); - }); - }); - }); - }); - - describe('listByAuthor()', function () { - it('should return author recent modules', function* () { - var rows = yield Module.listByAuthor('fengmk2'); - rows.forEach(function (r) { - r.should.have.keys('name', 'description'); - }); - }); - }); - - describe('updateReadme()', function () { - it('should update ok', function* () { - var row = yield Module.get(pkgname, pkgversion); - should.exist(row); - yield* Module.updateReadme(row.id, 'test'); - var data = yield Module.getById(row.id); - data.package.readme.should.equal('test'); - }); - }); - - describe('removeTagsByNames()', function () { - it('should work', function* () { - yield* Module.removeTagsByNames('foo', ['latest', '1.0']); - }); - }); - - describe('listPrivates()', function () { - it('should response [] if scopes not present', function* () { - mm(config, 'scopes', []); - var modules = yield Module.listPrivates(); - modules.should.eql([]); - }); - - it('should response [] if private modules not present', function* () { - mm(config, 'privatePackages', []); - mm(config, 'scopes', ['@not-exist']); - var modules = yield Module.listPrivates(); - modules.should.eql([]); - }); - - it('should work', function* () { - var modules = yield Module.listPrivates(); - modules.forEach(function (m) { - m.should.have.keys(['name', 'description']); - }); - }); - }); -}); diff --git a/test/services/package.test.js b/test/services/package.test.js new file mode 100644 index 0000000..8a91a63 --- /dev/null +++ b/test/services/package.test.js @@ -0,0 +1,453 @@ +/**! + * cnpmjs.org - test/services/package.test.js + * + * Copyright(c) fengmk2 and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 (http://fengmk2.github.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +var should = require('should'); +var sleep = require('co-sleep'); +var Package = require('../../services/package'); + +describe('services/package.test.js', function () { + function* createModule(name, version, user, tag) { + var sourcePackage = { + version: version, + name: name, + publish_time: Date.now(), + }; + var mod = { + version: sourcePackage.version, + name: sourcePackage.name, + package: sourcePackage, + author: user || 'unittest', + publish_time: sourcePackage.publish_time, + }; + var dist = { + tarball: 'http://registry.npmjs.org/' + name + '/-/' + name + '-' + version + '.tgz', + shasum: '9d7bc446e77963933301dd602d5731cb861135e0', + size: 100, + }; + mod.package.dist = dist; + yield* Package.saveModule(mod); + // add tag + yield* Package.addModuleTag(name, tag || 'latest', version); + return yield* Package.getModule(mod.name, mod.version); + } + + describe('addModuleTag()', function () { + it('should add latest tag to 1.0.0', function* () { + var r = yield* createModule('test-addModuleTag-module-name', '1.0.0'); + var tag = yield* Package.addModuleTag(r.name, 'latest', r.version); + should.exist(tag); + tag.should.have.keys('id', 'module_id', 'gmt_modified'); + tag.id.should.above(0); + + r = yield* createModule('test-addModuleTag-module-name', '1.1.0'); + var tag2 = yield* Package.addModuleTag(r.name, 'latest', r.version); + should.exist(tag2); + tag2.should.have.keys('id', 'module_id', 'gmt_modified'); + tag.id.should.equal(tag2.id); + + var tag3 = yield* Package.getModuleTag(r.name, 'latest'); + tag3.id.should.equal(tag2.id); + }); + + it('should return null when module not exists', function* () { + var tag = yield* Package.addModuleTag('not-exists', 'latest', '1.0.0'); + should.not.exist(tag); + }); + }); + + describe('getModuleByTag()', function () { + it('should get latest module', function* () { + var r = yield* createModule('test-getModuleByTag-module-name', '1.0.0'); + var tag = yield* Package.addModuleTag(r.name, 'latest', r.version); + should.exist(tag); + + var mod = yield* Package.getModuleByTag(r.name, 'latest'); + should.exist(mod); + mod.name.should.equal(r.name); + mod.version.should.equal(r.version); + mod.package.should.eql(r.package); + }); + + it('should return null when tag not exists', function* () { + var r = yield* Package.getModuleByTag('some', 'not-exists'); + should.not.exist(r); + }); + }); + + describe('listPublicModuleNamesByUser(), listPublicModulesByUser()', function () { + before(function* () { + yield* createModule('listPublicModuleNamesByUser-module0', '1.0.0', 'listPublicModuleNamesByUser-user'); + yield* createModule('listPublicModuleNamesByUser-module1', '1.0.0', 'listPublicModuleNamesByUser-user'); + yield* createModule('listPublicModuleNamesByUser-module2', '1.0.0', 'listPublicModuleNamesByUser-user'); + }); + + it('should got all public module names', function* () { + var names = yield* Package.listPublicModuleNamesByUser('listPublicModuleNamesByUser-user'); + names.should.length(3); + names.should.eql([ + 'listPublicModuleNamesByUser-module0', + 'listPublicModuleNamesByUser-module1', + 'listPublicModuleNamesByUser-module2' + ]); + }); + + it('should got all public modules', function* () { + var mods = yield* Package.listPublicModulesByUser('listPublicModuleNamesByUser-user'); + mods.should.length(3); + mods.forEach(function (mod) { + mod.toJSON().should.have.keys('name', 'description'); + mod.name.should.containEql('listPublicModuleNamesByUser-module'); + }); + }); + + it('should return [] when user not exists', function* () { + var mods = yield* Package.listPublicModulesByUser('listPublicModuleNamesByUser-user-not-exists'); + mods.should.length(0); + }); + }); + + describe('listModulesByName()', function () { + it('should return [] when module name not exists', function* () { + var mods = yield* Package.listModulesByName('not-exists-module'); + mods.should.length(0); + }); + + it('should return all version modules', function* () { + yield* createModule('test-listModulesByName-module-1', '1.0.0'); + yield* createModule('test-listModulesByName-module-1', '2.0.0'); + var modules = yield* Package.listModulesByName('test-listModulesByName-module-1'); + modules.should.length(2); + modules.forEach(function (mod) { + mod.package.name.should.equal(mod.name); + mod.name.should.containEql('test-listModulesByName-module-'); + }); + }); + }); + + describe('listPrivateModulesByScope()', function () { + it('should return [] when scope not exists', function* () { + var modules = yield* Package.listPrivateModulesByScope('@not-exists'); + modules.should.eql([]); + }); + + it('should work', function* () { + yield* createModule('@cnpm-test-scope1/test-listPrivateModules-module-1', '1.0.0'); + yield* createModule('@cnpm-test-scope1/test-listPrivateModules-module-2', '1.0.0'); + var modules = yield* Package.listPrivateModulesByScope('@cnpm-test-scope1'); + // console.log(modules[0].toJSON()) + modules.should.length(2); + modules[0].name.should.containEql('@cnpm-test-scope1/test-listPrivateModules-module-'); + }); + }); + + describe('listPublicModuleNamesSince(), listAllPublicModuleNames()', function () { + it('should got those module names', function* () { + var start = Date.now(); + yield* createModule('test-listPublicModuleNamesSince-module-0', '1.0.0'); + yield sleep(1100); + yield* createModule('test-listPublicModuleNamesSince-module-1', '1.0.0'); + yield* createModule('test-listPublicModuleNamesSince-module-1', '1.0.1', null, 'beta'); + yield* createModule('test-listPublicModuleNamesSince-module-2', '1.0.0'); + var names = yield* Package.listPublicModuleNamesSince(start); + names.should.length(2); + names.should.eql(['test-listPublicModuleNamesSince-module-1', 'test-listPublicModuleNamesSince-module-2']); + + var alls = yield* Package.listAllPublicModuleNames(); + alls.length.should.above(0); + alls.forEach(function (name) { + name.should.not.containEql('@'); + }); + }); + }); + + describe('getModuleLastModified()', function () { + it('should get a datetime', function* () { + yield* createModule('test-getModuleLastModified-module-0', '1.0.0'); + var t = yield* Package.getModuleLastModified('test-getModuleLastModified-module-0'); + t.should.be.a.Date; + }); + + it('should get null when module not exists', function* () { + var t = yield* Package.getModuleLastModified('test-getModuleLastModified-module-not-exists'); + should.ok(t === null); + }); + }); + + describe('removeModulesByName()', function () { + it('should remove all', function* () { + yield* createModule('test-removeModulesByName-module-1', '1.0.0'); + yield* createModule('test-removeModulesByName-module-1', '1.0.1', null, 'beta'); + yield* createModule('test-removeModulesByName-module-1', '2.0.0'); + + var mods = yield* Package.listModulesByName('test-removeModulesByName-module-1'); + mods.should.length(3); + yield* Package.removeModulesByName('test-removeModulesByName-module-1'); + mods = yield* Package.listModulesByName('test-removeModulesByName-module-1'); + mods.should.length(0); + }); + }); + + describe('removeModulesByNameAndVersions()', function () { + it('should remove some versions', function* () { + yield* createModule('test-removeModulesByNameAndVersions-module-1', '0.0.0'); + yield* createModule('test-removeModulesByNameAndVersions-module-1', '1.0.0'); + yield* createModule('test-removeModulesByNameAndVersions-module-1', '1.0.1', null, 'beta'); + yield* createModule('test-removeModulesByNameAndVersions-module-1', '2.0.0'); + + var mods = yield* Package.listModulesByName('test-removeModulesByNameAndVersions-module-1'); + mods.should.length(4); + + yield* Package.removeModulesByNameAndVersions('test-removeModulesByNameAndVersions-module-1', ['1.0.0']); + mods = yield* Package.listModulesByName('test-removeModulesByNameAndVersions-module-1'); + mods.should.length(3); + + yield* Package.removeModulesByNameAndVersions('test-removeModulesByNameAndVersions-module-1', + ['0.0.0', '1.0.0', '1.0.1', '2.0.0']); + mods = yield* Package.listModulesByName('test-removeModulesByNameAndVersions-module-1'); + mods.should.length(0); + }); + }); + + describe('removeModuleTagsByName()', function () { + it('should remove all tags by name', function* () { + var r2 = yield* createModule('test-removeModuleTagsByName2-module-name', '1.0.0'); + var tag = yield* Package.addModuleTag(r2.name, 'latest', r2.version); + should.exist(tag); + + var r = yield* createModule('test-removeModuleTagsByName-module-name', '1.0.0'); + var tag = yield* Package.addModuleTag(r.name, 'latest', r.version); + should.exist(tag); + var tag = yield* Package.addModuleTag(r.name, 'beta', r.version); + should.exist(tag); + + var tags = yield* Package.listModuleTags(r.name); + tags.should.length(2); + yield* Package.removeModuleTagsByName(r.name); + var tags = yield* Package.listModuleTags(r.name); + tags.should.eql([]); + + var tags2 = yield* Package.listModuleTags(r2.name); + tags2.should.length(1); + }); + }); + + describe('removeModuleTagsByIds()', function () { + it('should remove tags by ids', function* () { + var r = yield* createModule('test-removeModuleTagsByIds-module-name', '1.0.0'); + var tag1 = yield* Package.addModuleTag(r.name, 'latest', r.version); + should.exist(tag1); + var tag2 = yield* Package.addModuleTag(r.name, 'beta', r.version); + should.exist(tag2); + var tag3 = yield* Package.addModuleTag(r.name, 'beta2', r.version); + should.exist(tag3); + + var tags = yield* Package.listModuleTags(r.name); + tags.should.length(3); + yield* Package.removeModuleTagsByIds([tag1.id, tag3.id]); + var tags = yield* Package.listModuleTags(r.name); + tags.should.length(1); + tags[0].id.should.equal(tag2.id); + + yield* Package.removeModuleTagsByIds([tag2.id]); + tags = yield* Package.listModuleTags(r.name); + tags.should.length(0); + }); + }); + + describe('removeModuleTagsByNames()', function () { + it('should remove some tags', function* () { + var r = yield* createModule('test-removeModuleTagsByNames-module-name', '1.0.0'); + var tag1 = yield* Package.addModuleTag(r.name, 'latest', r.version); + should.exist(tag1); + var tag2 = yield* Package.addModuleTag(r.name, 'beta', r.version); + should.exist(tag2); + var tag3 = yield* Package.addModuleTag(r.name, 'beta2', r.version); + should.exist(tag3); + + yield* Package.removeModuleTagsByNames(r.name, ['beta', 'beta2']); + var tags = yield* Package.listModuleTags(r.name); + tags.should.length(1); + + yield* Package.removeModuleTagsByNames(r.name, ['beta', 'beta2', 'latest']); + var tags = yield* Package.listModuleTags(r.name); + tags.should.length(0); + }); + }); + + describe('addModule()', function () { + it('should success ad he@0.3.6', function* () { + var sourcePackage = require('../fixtures/0.3.6.json'); + var mod = { + version: sourcePackage.version, + name: sourcePackage.name, + package: sourcePackage, + author: 'unittest', + publish_time: sourcePackage.publish_time || Date.now(), + }; + var dist = { + tarball: 'http://registry.npmjs.org/he/-/he-0.3.6.tgz', + shasum: '9d7bc446e77963933301dd602d5731cb861135e0', + size: 100, + }; + mod.package.dist = dist; + var result = yield* Package.saveModule(mod); + result.id.should.be.a.Number; + var item = yield* Package.getModuleById(result.id); + item.dist_size.should.equal(dist.size); + item.dist_shasum.should.equal(dist.shasum); + item.package.readme.should.equal(sourcePackage.readme); + + // get by name and version + var r = yield* Package.getModule(mod.name, mod.version); + r.package.readme.should.equal(sourcePackage.readme); + }); + }); + + describe('updateModulePackage()', function () { + it('should update not exists package return null', function* () { + var r = yield* Package.updateModulePackage(101010101, {}); + should.not.exist(r); + }); + + it('should update exists package', function* () { + var sourcePackage = { + version: '1.0.0', + name: 'test-update-module-package-name', + publish_time: Date.now(), + }; + var mod = { + version: sourcePackage.version, + name: sourcePackage.name, + package: sourcePackage, + author: 'unittest', + publish_time: sourcePackage.publish_time, + }; + var dist = { + tarball: 'http://registry.npmjs.org/he/-/he-0.3.6.tgz', + shasum: '9d7bc446e77963933301dd602d5731cb861135e0', + size: 100, + }; + mod.package.dist = dist; + yield* Package.saveModule(mod); + var result = yield* Package.getModule(mod.name, mod.version); + result.package.should.eql(sourcePackage); + + var newPackage = {foo: 'bar'}; + yield* Package.updateModulePackage(result.id, newPackage); + + var r = yield* Package.getModule(mod.name, mod.version); + r.package.should.eql(newPackage); + }); + }); + + describe('updateModulePackageFields()', function () { + it('should return null when update not exists module', function* () { + var r = yield* Package.updateModulePackageFields(123123123, {foo: 'bar'}); + should.not.exist(r); + }); + + it('should return updated module instance', function* () { + var r = yield* createModule('test-updateModulePackageFields-name', '1.0.0'); + should.exist(r); + var r1 = yield* Package.updateModulePackageFields(r.id, {foo: 'update for field'}); + r1.id.should.equal(r.id); + var r2 = yield* Package.getModuleById(r1.id); + r2.package.should.have.property('foo', 'update for field'); + }); + }); + + describe('updateModuleReadme()', function () { + it('should return null when update not exists module', function* () { + var r = yield* Package.updateModuleReadme(123123123, 'test updateModuleReadme'); + should.not.exist(r); + }); + + it('should return updated module instance', function* () { + var r = yield* createModule('test-updateModuleReadme-name', '1.0.0'); + should.exist(r); + var r1 = yield* Package.updateModuleReadme(r.id, 'test updateModuleReadme'); + r1.id.should.equal(r.id); + var r2 = yield* Package.getModuleById(r1.id); + r2.package.readme.should.equal('test updateModuleReadme'); + }); + }); + + describe('updateModuleDescription()', function () { + it('should return null when update not exists module', function* () { + var r = yield* Package.updateModuleDescription(123123123, 'test updateModuleDescription'); + should.not.exist(r); + }); + + it('should return updated module instance', function* () { + var r = yield* createModule('test-updateModuleDescription-name', '1.0.0'); + should.exist(r); + var r1 = yield* Package.updateModuleDescription(r.id, 'test updateModuleDescription'); + r1.id.should.equal(r.id); + var r2 = yield* Package.getModuleById(r1.id); + r2.description.should.equal('test updateModuleDescription'); + r2.package.description.should.equal('test updateModuleDescription'); + }); + }); + + describe('updateModuleLastModified()', function () { + it('should return null when module not exists', function* () { + var r = yield* Package.updateModuleLastModified('not-exists-module-name'); + should.not.exist(r); + }); + + it('should return the update module when update lastTime exists', function* () { + var r1 = yield* createModule('test-update-module-last-modified-package-name', '1.0.0'); + yield sleep(1100); + yield* Package.updateModuleLastModified(r1.name); + var r2 = yield* Package.getModule(r1.name, r1.version); + r2.gmt_modified.getTime().should.above(r1.gmt_modified.getTime()); + }); + }); + + describe('search()', function () { + before(function* () { + yield* Package.addKeywords('aaaa', 'mock aaaaaa', ['aa', 'bb', 'cc']); + }); + + it('should search modules', function* () { + var data = yield* Package.search('test'); + data.should.have.keys('keywordMatchs', 'searchMatchs'); + data.searchMatchs.length.should.above(0); + data.searchMatchs.forEach(function (row) { + row.name.should.be.a.String; + row.name.indexOf('test').should.above(-1); + row.description.should.be.a.String; + }); + }); + + it('should search match keywords modules', function* () { + var data = yield* Package.search('aa'); + data.keywordMatchs.length.should.above(0); + data.keywordMatchs.forEach(function (row) { + row.name.should.be.a.String; + row.description.should.be.a.String; + }); + }); + + it('should search return empty', function* () { + var data = yield* Package.search('emptyemptyemptyempty'); + data.should.eql({ + keywordMatchs: [], + searchMatchs: [] + }); + }); + }); +}); diff --git a/test/services/total.test.js b/test/services/total.test.js index 529f787..ba44b38 100644 --- a/test/services/total.test.js +++ b/test/services/total.test.js @@ -16,7 +16,7 @@ var Total = require('../../services/total'); -describe.only('services/total.test.js', function () { +describe('services/total.test.js', function () { describe('plusDeleteModule()', function () { it('should plus delete module count', function* () { var info = yield* Total.getTotalInfo();