From bc8aed6b4b25c115dd0e22cb52c4b8ebfaae59fa Mon Sep 17 00:00:00 2001 From: chencheng Date: Mon, 17 Nov 2014 17:49:23 +0800 Subject: [PATCH 01/12] feat(package): add semver support --- routes/index.js | 7 ++++++- routes/repository.js | 10 +++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/routes/index.js b/routes/index.js index 178ec73..92acc19 100644 --- a/routes/index.js +++ b/routes/index.js @@ -9,6 +9,7 @@ var fs = require('fs'); var path = require('path'); var request = require('request'); var elastical = require('elastical'); +var semver = require('semver'); var client = new elastical.Client(); var badge = require('../lib/badge'); var anonymous = CONFIG.authorize.type === 'anonymous'; @@ -153,7 +154,11 @@ exports.project = function(req, res, next) { exports.package = function(req, res, next) { var name = req.params.name; - var version = req.params.version; + var project = new Project({ + name: req.params.name + }); + var version = semver.maxSatisfying(Object.keys(project.packages), req.params.version); + var p = new Package({ name: name, version: version diff --git a/routes/repository.js b/routes/repository.js index 1e8b29f..b1c8168 100755 --- a/routes/repository.js +++ b/routes/repository.js @@ -5,6 +5,7 @@ var fs = require('fs-extra'); var path = require('path'); var tempfile = require('tempfile'); var tar = require('tarball-extract'); +var semver = require('semver'); var hook = require('../lib/hook'); var history = require('../lib/history'); var elastical = require('elastical'); @@ -68,9 +69,14 @@ exports.project = { var Cache = {}; exports.package = { get: function(req, res) { + var project = new Project({ + name: req.params.name + }); + var version = semver.maxSatisfying(Object.keys(project.packages), req.params.version); + var p = new Package({ name: req.params.name, - version: req.params.version + version: version }); if (!p.md5) { abortify(res, { code: 404 }); @@ -370,3 +376,5 @@ function abortify(res, options) { message: message }); } + + From 9a79839537043a2e93ee0418cf60600fde2b7fba Mon Sep 17 00:00:00 2001 From: chencheng Date: Mon, 17 Nov 2014 18:27:24 +0800 Subject: [PATCH 02/12] feat(package): remove force publish --- routes/repository.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/routes/repository.js b/routes/repository.js index b1c8168..731776d 100755 --- a/routes/repository.js +++ b/routes/repository.js @@ -148,8 +148,7 @@ exports.package = { Cache.package = new Package(data); - var force = req.headers['x-yuan-force']; - if(Cache.package.md5 && !force) { + if(Cache.package.md5) { return abortify(res, { code: 444 }); } @@ -367,7 +366,7 @@ function abortify(res, options) { 406: 'Not acceptable.', 415: 'Unsupported media type.', 426: 'Upgrade required.', - 444: 'Force option required.' + 444: 'Cannot modify pre-existing version.' }; message = options.message || msgs[code]; res.send(code, { From 8ba5fa904c846c7d5dd9d3aabffc8780470fcac9 Mon Sep 17 00:00:00 2001 From: chencheng Date: Tue, 18 Nov 2014 15:52:31 +0800 Subject: [PATCH 03/12] feat(unpublish): add unpublished instead of rmrf --- models/package.js | 7 ++++++- models/project.js | 7 ++++++- routes/repository.js | 3 +-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/models/package.js b/models/package.js index d54f52d..3c2a644 100644 --- a/models/package.js +++ b/models/package.js @@ -43,7 +43,12 @@ Package.prototype = { }, delete: function() { - fs.removeSync(path.join(CONFIG.wwwroot, 'repository', this.name, this.version)); + var now = moment().format('YYYY-MM-DDTHH:mm:ssZ'); + this['unpublished'] = { + time: now + }; + fs.outputJsonSync(this.datafile(), this); + fs.removeSync(path.join(CONFIG.wwwroot, 'repository', this.name, this.version, this.filename)); return this; } }; diff --git a/models/project.js b/models/project.js index ff68280..1b394b9 100644 --- a/models/project.js +++ b/models/project.js @@ -38,7 +38,12 @@ Project.prototype = { this.getVersions().forEach(function(version) { that.remove(version); }); - fs.removeSync(path.join(CONFIG.wwwroot, 'repository', this.name)); + + var now = moment().format('YYYY-MM-DDTHH:mm:ssZ'); + this['unpublished'] = { + time: now + }; + fs.outputJsonSync(this.datafile(), this); return this; }, diff --git a/routes/repository.js b/routes/repository.js index 731776d..9e4c88d 100755 --- a/routes/repository.js +++ b/routes/repository.js @@ -152,20 +152,19 @@ exports.package = { return abortify(res, { code: 444 }); } - var isNewProject; Cache.project = new Project(data); var isNewProject = !Cache.project['created_at']; if (isNewProject) { data.owners = [data.publisher]; } Cache.project.update(data); + delete Cache.project.unpublished; if (isNewProject) { hook.emit('create:project', Cache.project, data.publisher); } res.send(200, {}); }, put: function(req, res) { - var data = req.body; var package = Cache.package; var project = Cache.project; if (!package) { From 01fb67ee6e2ea9c23d36f430e4c9a23a2a6696f2 Mon Sep 17 00:00:00 2001 From: chencheng Date: Tue, 18 Nov 2014 18:28:38 +0800 Subject: [PATCH 04/12] feat(view): add unpublished state for package --- routes/index.js | 3 +++ views/project.ejs | 21 ++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/routes/index.js b/routes/index.js index 92acc19..eacb3c3 100644 --- a/routes/index.js +++ b/routes/index.js @@ -123,6 +123,9 @@ exports.project = function(req, res, next) { p.latest.dependents = _.uniq((p.latest.dependents || []).map(function(d) { return d.split('@')[0]; })); + if (p.unpublished) { + p.unpublished.fromNow = moment(p.unpublished.time).fromNow(); + } var editable; if (p.owners && p.owners.length > 0 && req.session.user && diff --git a/views/project.ejs b/views/project.ejs index 3fa132c..4611ca9 100644 --- a/views/project.ejs +++ b/views/project.ejs @@ -18,13 +18,31 @@ <% } %>
+ <% if (project.unpublished) { %> +

This package has been unpublished.

+ <% } else { %>

<%= project.description || "No description" %>

spm install <%= project.name %>

Latest version: <%= project.latest.version %> ~ <%= project.latest.tag %>

+ <% } %> + <% if (project.unpublished) { %> + + + + + + + + + <% } else { %> <% } %> - <% if (project.latest.spm.main) { %> + <% } %> + <% if (project.latest.spm && project.latest.spm.main) { %> From a5087521a76da486c9ee0aeec13ece4efb88249b Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 25 Nov 2014 17:01:26 +0800 Subject: [PATCH 05/12] pedantic option of marked for not parsing long html string, fix #1067 --- routes/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/routes/index.js b/routes/index.js index eacb3c3..1393eda 100644 --- a/routes/index.js +++ b/routes/index.js @@ -30,6 +30,7 @@ renderer.heading = function(text, level) { // Synchronous highlighting with highlight.js marked.setOptions({ + pedantic: true, highlight: function (code) { return require('highlight.js').highlightAuto(code).value; } From 88ba2646a95c630f696489281675ac5789f31b73 Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 25 Nov 2014 17:31:52 +0800 Subject: [PATCH 06/12] update deps and fix app.js --- app.js | 11 ++++++++--- package.json | 42 +++++++++++++++++++++--------------------- routes/account.js | 10 +++++----- routes/repository.js | 24 ++++++++++++------------ 4 files changed, 46 insertions(+), 41 deletions(-) diff --git a/app.js b/app.js index 2ff9ed7..a16ec3f 100644 --- a/app.js +++ b/app.js @@ -51,14 +51,19 @@ app.set('port', CONFIG.server.port || 3000); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); app.use(favicon(__dirname + '/public/favicon.ico')); -app.use(morgan()); +app.use(morgan('combined')); app.use(require('./middlewares/raw-body')({ contentTypes: ['application/x-tar'], limit: '50mb' })); -app.use(bodyParser()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(bodyParser.json()); app.use(cookieParser('spmjs')); -app.use(session({ secret: 'keyboard cat', key: 'sid' })); +app.use(session({ + secret: 'keyboard cat', + resave: false, + saveUninitialized: true +})); app.use(serveStatic(path.join(__dirname, 'public'))); // development only diff --git a/package.json b/package.json index 6861ace..716716c 100644 --- a/package.json +++ b/package.json @@ -34,39 +34,39 @@ }, "dependencies": { "async": "~0.9.0", - "body-parser": "~1.3.0", + "body-parser": "^1.9.3", "capitalize": "~0.5.0", - "connect-multiparty": "~1.0.3", - "cookie-parser": "~1.1.0", - "debug": "~1.0.4", + "connect-multiparty": "^1.2.5", + "cookie-parser": "^1.3.3", + "debug": "^2.1.0", "ejs": "*", "elastical": "0.0.13", - "errorhandler": "~1.0.0", - "express": "~4.4.1", - "express-session": "~1.2.0", - "fs-extra": "~0.11.0", - "github-token": "~0.1.0", + "errorhandler": "^1.3.0", + "express": "^4.10.4", + "express-session": "^1.9.2", + "fs-extra": "^0.12.0", + "github-token": "~0.1.1", "githuburl": "~0.1.6", - "highlight.js": "~8.0.0", + "highlight.js": "^8.4.0", "lodash": "~2.4.1", "marked": "~0.3.2", "mkdirp": "~0.5.0", - "moment": "~2.6.0", - "morgan": "~1.1.0", - "ms": "~0.6.2", - "nedb": "~0.10.5", + "moment": "^2.8.4", + "morgan": "^1.5.0", + "ms": "^0.7.0", + "nedb": "~0.11.1", "node-yaml-config": "~0.0.2", - "raw-body": "~1.1.4", - "request": "~2.39.0", + "raw-body": "^1.3.1", + "request": "^2.48.0", "rimraf": "~2.2.8", - "semver": "~3.0.0", - "serve-spm": "~0.7.1", + "semver": "^4.1.0", + "serve-spm": "~0.7.5", "serve-static": "~1.2.1", - "serve-favicon": "~2.0.1", + "serve-favicon": "^2.1.7", "tarball-extract": "~0.0.3", - "tempfile": "~1.0.0" + "tempfile": "^1.1.0" }, "devDependencies": { - "forever": "~0.11.1" + "forever": "*" } } diff --git a/routes/account.js b/routes/account.js index 6eaaf88..ac1649a 100644 --- a/routes/account.js +++ b/routes/account.js @@ -97,11 +97,11 @@ exports.authorize = function(req, res) { var authkey = req.body.authkey; account.authorize(id, authkey, function(result) { if (result) { - res.send(200, { + res.status(200).send({ data: authkey }); } else { - res.send(403, { + res.status(403).send({ message: 'username or authkey is wrong.' }); } @@ -110,7 +110,7 @@ exports.authorize = function(req, res) { exports.ownership = function(req, res) { if (!req.session.user) { - res.send(401); + res.status(401).send(); return; } var errormessage; @@ -130,11 +130,11 @@ exports.ownership = function(req, res) { } if (req.headers['x-requested-with'] === 'XMLHttpRequest') { if (errormessage) { - res.send(403, { + res.status(403).send({ errormessage: errormessage }); } else { - res.send(200); + res.status(200).send(); } } else { res.redirect(url.parse(req.headers.referer).pathname + errormessage); diff --git a/routes/repository.js b/routes/repository.js index 9e4c88d..2e5422e 100755 --- a/routes/repository.js +++ b/routes/repository.js @@ -20,7 +20,7 @@ exports.index = function(req, res) { res.set('Content-Type', 'application/javascript'); data = 'define(' + data + ');'; } - res.send(200, data); + res.status(200).send(data); }; exports.since = function(req, res, next) { @@ -30,7 +30,7 @@ exports.since = function(req, res, next) { } history.updateAfter(updateAfter, function(data) { res.set('Content-Type', 'application/javascript'); - res.send(200, data); + res.status(200).send(data); }); }; @@ -48,7 +48,7 @@ exports.project = { res.set('Content-Type', 'application/javascript'); data = 'define(' + data + ');'; } - res.send(200, data); + res.status(200).send(data); } }, delete: function(req, res) { @@ -58,7 +58,7 @@ exports.project = { } else { hook.emit('delete:project', project, req.body.publisher); project.delete(); - res.send(200, { + res.status(200).send({ status: 'info', message: 'Project is deleted.' }); @@ -87,7 +87,7 @@ exports.package = { res.set('Content-Type', 'application/javascript'); data = 'define(' + data + ');'; } - res.send(200, data); + res.status(200).send(data); } }, checkPermission: function(req, res, next) { @@ -162,7 +162,7 @@ exports.package = { if (isNewProject) { hook.emit('create:project', Cache.project, data.publisher); } - res.send(200, {}); + res.status(200).send({}); }, put: function(req, res) { var package = Cache.package; @@ -203,7 +203,7 @@ exports.package = { project.save(); hook.emit('update:package', package); - res.send(200, package); + res.status(200).send(package); }, delete: function(req, res) { var package = new Package(req.params); @@ -216,7 +216,7 @@ exports.package = { if (leftPackageCount <= 0) { hook.emit('delete:project', project, req.body.publisher); } - res.send(200, { + res.status(200).send({ status: 'info', message: 'Package is deleted.' }); @@ -289,7 +289,7 @@ exports.upload = function(req, res) { fs.copySync(fpath, dest); - res.send(200, { + res.status(200).send({ status: 'info', message: 'Upload docs success.' }); @@ -335,7 +335,7 @@ exports.search = function(req, res, next) { data = JSON.stringify(data, null, 2); data = 'define(' + data + ');'; } - res.send(200, data); + res.status(200).send(data); }); }; @@ -345,7 +345,7 @@ exports.data = function(req, res) { projects: query.projects ? query.projects.split(",") : null, fields: query.fields ? query.fields.split(",") : null }); - res.send(200, { + res.status(200).send({ data: { total: data.length, results: data @@ -368,7 +368,7 @@ function abortify(res, options) { 444: 'Cannot modify pre-existing version.' }; message = options.message || msgs[code]; - res.send(code, { + res.status(code).send({ statusCode: code, status: status, message: message From ff3baea1127b9c69627e0f0898db040f6ee41e69 Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 25 Nov 2014 17:42:44 +0800 Subject: [PATCH 07/12] update history for 1.3.0-dev --- History.md | 9 +++++++++ package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index d01e901..6179afd 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,12 @@ +1.3.0 / 开发中 +================== + +* 提升了首页的加载性能。https://github.com/spmjs/spmjs.io/commit/573d7d39dc32ae1d7cfcfc2aff872e8220e0f436 +* 修复解析 md 文件里长 html 字符串时太慢的问题。 spmjs/spm#1067 +* 简化了 badge 的文案。 +* 改进了 /repository/search 的输出格式。https://github.com/spmjs/spmjs.io/commit/7d91382dd6696e98c21f4e9713f6272bc5d9506d +* 部分依赖升级。 + 1.2.0 / 2014-09-09 ================== diff --git a/package.json b/package.json index 716716c..c048109 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "spmjs.io", "descrition": "The packaging server for spm@3.x", "private": true, - "version": "1.2.0", + "version": "1.3.0-dev", "author": "afc163 ", "keywords": [ "spm", From a6a22bbd0f0f3562ce08cfe0d2ad6ad874cc8673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=81=8F=E5=8F=B3?= Date: Tue, 25 Nov 2014 17:49:39 +0800 Subject: [PATCH 08/12] Update History.md --- History.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index 6179afd..144f72a 100644 --- a/History.md +++ b/History.md @@ -1,10 +1,10 @@ 1.3.0 / 开发中 ================== -* 提升了首页的加载性能。https://github.com/spmjs/spmjs.io/commit/573d7d39dc32ae1d7cfcfc2aff872e8220e0f436 +* [提升了首页的加载性能](https://github.com/spmjs/spmjs.io/commit/573d7d39dc32ae1d7cfcfc2aff872e8220e0f436)。 * 修复解析 md 文件里长 html 字符串时太慢的问题。 spmjs/spm#1067 * 简化了 badge 的文案。 -* 改进了 /repository/search 的输出格式。https://github.com/spmjs/spmjs.io/commit/7d91382dd6696e98c21f4e9713f6272bc5d9506d +* [改进了 `/repository/search` 的输出格式](https://github.com/spmjs/spmjs.io/commit/7d91382dd6696e98c21f4e9713f6272bc5d9506d)。 * 部分依赖升级。 1.2.0 / 2014-09-09 From a191fed809256243954d1a0762993da4243e978c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=81=8F=E5=8F=B3?= Date: Tue, 25 Nov 2014 17:57:41 +0800 Subject: [PATCH 09/12] Update History.md --- History.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/History.md b/History.md index 144f72a..ca2f604 100644 --- a/History.md +++ b/History.md @@ -2,7 +2,7 @@ ================== * [提升了首页的加载性能](https://github.com/spmjs/spmjs.io/commit/573d7d39dc32ae1d7cfcfc2aff872e8220e0f436)。 -* 修复解析 md 文件里长 html 字符串时太慢的问题。 spmjs/spm#1067 +* 修复解析 md 文件里长 html 字符串时太慢的[问题](https://github.com/spmjs/spm/issues/1067)。 * 简化了 badge 的文案。 * [改进了 `/repository/search` 的输出格式](https://github.com/spmjs/spmjs.io/commit/7d91382dd6696e98c21f4e9713f6272bc5d9506d)。 * 部分依赖升级。 From ecc88b2e5d29ef0dff848bafd27aafb8770df7c3 Mon Sep 17 00:00:00 2001 From: afc163 Date: Wed, 3 Dec 2014 10:05:23 +0800 Subject: [PATCH 10/12] trim account name --- routes/account.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/account.js b/routes/account.js index ac1649a..81f0476 100644 --- a/routes/account.js +++ b/routes/account.js @@ -93,7 +93,7 @@ exports.logout = function(req, res) { // for spm login exports.authorize = function(req, res) { - var id = req.body.account; + var id = req.body.account.trim(); var authkey = req.body.authkey; account.authorize(id, authkey, function(result) { if (result) { From 8446a19ea6d113ec55b8db8e380ceba33e1d570a Mon Sep 17 00:00:00 2001 From: chencheng Date: Tue, 16 Dec 2014 17:32:30 +0800 Subject: [PATCH 11/12] Revert "feat(package): remove force publish" This reverts commit 9a79839537043a2e93ee0418cf60600fde2b7fba. --- routes/repository.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/routes/repository.js b/routes/repository.js index 2e5422e..a4328d1 100755 --- a/routes/repository.js +++ b/routes/repository.js @@ -148,7 +148,8 @@ exports.package = { Cache.package = new Package(data); - if(Cache.package.md5) { + var force = req.headers['x-yuan-force']; + if(Cache.package.md5 && !force) { return abortify(res, { code: 444 }); } @@ -365,7 +366,7 @@ function abortify(res, options) { 406: 'Not acceptable.', 415: 'Unsupported media type.', 426: 'Upgrade required.', - 444: 'Cannot modify pre-existing version.' + 444: 'Force option required.' }; message = options.message || msgs[code]; res.status(code).send({ From aed50af2027252e17fa6e3f7dd660f747c5dca22 Mon Sep 17 00:00:00 2001 From: chencheng Date: Tue, 16 Dec 2014 18:38:55 +0800 Subject: [PATCH 12/12] add admin list that can use publish --force --- config/base.default.yaml | 2 ++ routes/repository.js | 16 +++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/config/base.default.yaml b/config/base.default.yaml index c4891cd..20c2147 100644 --- a/config/base.default.yaml +++ b/config/base.default.yaml @@ -17,6 +17,8 @@ default: whitelistOnly: off whitelist: + admin: + sync: off syncInterval: '10m' syncSource: 'http://spmjs.io' diff --git a/routes/repository.js b/routes/repository.js index a4328d1..d8443ef 100755 --- a/routes/repository.js +++ b/routes/repository.js @@ -122,9 +122,15 @@ exports.package = { if (!publisher) { return abortify(res, { code: 401 }); } - var permission = (!p.created_at) || account.checkPermission(publisher, name); - if (!permission) { - return abortify(res, { code: 403 }); + + var isAdmin = 'admin' in CONFIG && CONFIG.admin.split(' ').indexOf(publisher) > -1; + if (isAdmin) { + req.body.isAdmin = true; + } else { + var permission = (!p.created_at) || account.checkPermission(publisher, name); + if (!permission) { + return abortify(res, { code: 403 }); + } } req.body.publisher = publisher; next(); @@ -149,7 +155,7 @@ exports.package = { Cache.package = new Package(data); var force = req.headers['x-yuan-force']; - if(Cache.package.md5 && !force) { + if(Cache.package.md5 && (!req.body.isAdmin || (req.body.isAdmin && !force))) { return abortify(res, { code: 444 }); } @@ -366,7 +372,7 @@ function abortify(res, options) { 406: 'Not acceptable.', 415: 'Unsupported media type.', 426: 'Upgrade required.', - 444: 'Force option required.' + 444: 'Cannot modify pre-existing version.' }; message = options.message || msgs[code]; res.status(code).send({
Description + <%= project.description || "No description" %> +
Unpublished On + <%= project.unpublished.fromNow %> +
Versions @@ -81,7 +99,8 @@
Main <%= project.latest.spm.main %>