UNPKG

cnpmjs.org

Version:

Private npm registry and web for Enterprise, base on MySQL and Simple Store Service

713 lines (622 loc) 16.6 kB
/**! * 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 models = require('../models'); var common = require('./common'); var Tag = models.Tag; var User = models.User; var Module = models.Module; var ModuleStar = models.ModuleStar; var ModuleKeyword = models.ModuleKeyword; var PrivateModuleMaintainer = models.ModuleMaintainer; var ModuleDependency = models.ModuleDependency; var ModuleUnpublished = models.ModuleUnpublished; var NpmModuleMaintainer = models.NpmModuleMaintainer; // 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); }; exports.getLatestModule = function* (name) { return yield* exports.getModuleByTag(name, 'latest'); }; // 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.listModules = function* (names) { 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; }); var rows = yield Module.findAll({ where: { id: ids }, attributes: [ 'name', 'description' ] }); return rows; }; exports.listModulesByUser = function* (username) { var names = yield* exports.listModuleNamesByUser(username); return yield* exports.listModules(names); }; exports.listModuleNamesByUser = 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; }); // find from npm module maintainer table var moduleNames = yield* NpmModuleMaintainer.listModuleNamesByUser(username); moduleNames.forEach(function (name) { if (!map[name]) { names.push(name); } }); // find from private module maintainer table moduleNames = yield* PrivateModuleMaintainer.listModuleNamesByUser(username); moduleNames.forEach(function (name) { if (!map[name]) { names.push(name); } }); return names; }; exports.listPublicModulesByUser = function* (username) { var names = yield* exports.listPublicModuleNamesByUser(username); return yield* exports.listModules(names); }; // 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 moduleNames = yield* NpmModuleMaintainer.listModuleNamesByUser(username); moduleNames.forEach(function (name) { if (!map[name]) { names.push(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 mod = yield Module.find({ where: { name: name, }, order: [ ['gmt_modified', 'DESC'] ], attributes: [ 'gmt_modified' ] }); return mod && mod.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; if (item.isDirty) { item = yield item.save(); } var result = { id: item.id, gmt_modified: item.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; if (row.isDirty) { return yield row.save(); } return row; }; exports.getModuleTag = function* (name, tag) { return yield Tag.findByNameAndTag(name, tag); }; exports.removeModuleTags = 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 } }); }; // dependencies // name => dependency exports.addDependency = function* (name, dependency) { var row = yield ModuleDependency.find({ where: { name: dependency, dependent: name } }); if (row) { return row; } return yield ModuleDependency.build({ name: dependency, dependent: name }).save(); }; exports.addDependencies = function* (name, dependencies) { var tasks = []; for (var i = 0; i < dependencies.length; i++) { tasks.push(exports.addDependency(name, dependencies[i])); } return yield tasks; }; exports.listDependents = function* (dependency) { var items = yield ModuleDependency.findAll({ where: { name: dependency } }); return items.map(function (item) { return item.dependent; }); }; // maintainers exports.listPublicModuleMaintainers = function* (name) { return yield* NpmModuleMaintainer.listMaintainers(name); }; exports.addPublicModuleMaintainer = function* (name, user) { return yield* NpmModuleMaintainer.addMaintainer(name, user); }; exports.removePublicModuleMaintainer = function* (name, user) { return yield* NpmModuleMaintainer.removeMaintainers(name, user); }; // only can add to cnpm maintainer table exports.addPrivateModuleMaintainers = function* (name, usernames) { return yield* PrivateModuleMaintainer.addMaintainers(name, usernames); }; exports.updatePrivateModuleMaintainers = function* (name, usernames) { var result = yield* PrivateModuleMaintainer.updateMaintainers(name, usernames); if (result.add.length > 0 || result.remove.length > 0) { yield* exports.updateModuleLastModified(name); } return result; }; function* getMaintainerModel(name) { var isPrivatePackage = yield* common.isPrivatePackage(name); return isPrivatePackage ? PrivateModuleMaintainer : NpmModuleMaintainer; } exports.listMaintainers = function* (name) { var mod = yield* getMaintainerModel(name); var usernames = yield* mod.listMaintainers(name); if (usernames.length === 0) { return usernames; } var users = yield* User.listByNames(usernames); return users.map(function (user) { return { name: user.name, email: user.email }; }); }; exports.listMaintainerNamesOnly = function* (name) { var mod = yield* getMaintainerModel(name); return yield* mod.listMaintainers(name); }; exports.removeAllMaintainers = function* (name) { return yield [ PrivateModuleMaintainer.removeAllMaintainers(name), NpmModuleMaintainer.removeAllMaintainers(name), ]; }; exports.authMaintainer = function* (packageName, username) { var mod = yield* getMaintainerModel(packageName); var rs = yield [ mod.listMaintainers(packageName), exports.getLatestModule(packageName) ]; var maintainers = rs[0]; var latestMod = rs[1]; if (maintainers.length === 0) { // if not found maintainers, try to get from latest module package info var ms = latestMod && latestMod.package && latestMod.package.maintainers; if (ms && ms.length > 0) { maintainers = ms.map(function (user) { return user.name; }); } } var isMaintainer = false; if (latestMod && !latestMod.package._publish_on_cnpm) { // no one can update public package maintainers // public package only sync from source npm registry isMaintainer = false; } else if (maintainers.length === 0) { // no maintainers, meaning this module is free for everyone isMaintainer = true; } else if (maintainers.indexOf(username) >= 0) { isMaintainer = true; } return { isMaintainer: isMaintainer, maintainers: maintainers }; }; 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; if (item.isDirty) { // make sure object will change, otherwise will cause empty sql error // @see https://github.com/cnpm/cnpmjs.org/issues/533 return yield item.save(); } return item; }; 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; }; // module star exports.addStar = function* add(name, user) { var row = yield ModuleStar.find({ where: { name: name, user: user } }); if (row) { return row; } row = ModuleStar.build({ name: name, user: user }); return yield row.save(); }; exports.removeStar = function* (name, user) { return yield ModuleStar.destroy({ where: { name: name, user: user } }); }; exports.listStarUserNames = function* (moduleName) { var rows = yield ModuleStar.findAll({ where: { name: moduleName } }); return rows.map(function (row) { return row.user; }); }; exports.listUserStarModuleNames = function* (user) { var rows = yield ModuleStar.findAll({ where: { user: user } }); return rows.map(function (row) { return row.name; }); }; // unpublish info exports.saveUnpublishedModule = function* (name, pkg) { return yield* ModuleUnpublished.save(name, pkg); }; exports.getUnpublishedModule = function* (name) { return yield* ModuleUnpublished.findByName(name); };