mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-30 21:40:39 +03:00
078f464197
covers 90% of #755 - moved ghost.settings to api.settings - moved ghost.notifications to api.notifications - split up api/index.js to notifications.js, posts.js, settings.js, tags.js and users.js - added instance.globals as temp workaround for blogglobals (Known issue: blog title and blog description are updated after restart only) - added webroot to config() to remove `var root = ...` - changed `e` and `url` helper to async - updated tests
312 lines
11 KiB
JavaScript
312 lines
11 KiB
JavaScript
var config = require('../config'),
|
|
_ = require('underscore'),
|
|
path = require('path'),
|
|
when = require('when'),
|
|
api = require('../api'),
|
|
mailer = require('../mail'),
|
|
errors = require('../errorHandling'),
|
|
storage = require('../storage'),
|
|
|
|
adminNavbar,
|
|
adminControllers,
|
|
loginSecurity = [];
|
|
|
|
// TODO: combine path/navClass to single "slug(?)" variable with no prefix
|
|
adminNavbar = {
|
|
content: {
|
|
name: 'Content',
|
|
navClass: 'content',
|
|
key: 'admin.navbar.content',
|
|
path: '/'
|
|
},
|
|
add: {
|
|
name: 'New Post',
|
|
navClass: 'editor',
|
|
key: 'admin.navbar.editor',
|
|
path: '/editor/'
|
|
},
|
|
settings: {
|
|
name: 'Settings',
|
|
navClass: 'settings',
|
|
key: 'admin.navbar.settings',
|
|
path: '/settings/'
|
|
}
|
|
};
|
|
|
|
|
|
// TODO: make this a util or helper
|
|
function setSelected(list, name) {
|
|
_.each(list, function (item, key) {
|
|
item.selected = key === name;
|
|
});
|
|
return list;
|
|
}
|
|
|
|
adminControllers = {
|
|
'uploader': function (req, res) {
|
|
var type = req.files.uploadimage.type,
|
|
ext = path.extname(req.files.uploadimage.name).toLowerCase(),
|
|
store = storage.get_storage();
|
|
|
|
if ((type !== 'image/jpeg' && type !== 'image/png' && type !== 'image/gif' && type !== 'image/svg+xml')
|
|
|| (ext !== '.jpg' && ext !== '.jpeg' && ext !== '.png' && ext !== '.gif' && ext !== '.svg' && ext !== '.svgz')) {
|
|
return res.send(415, 'Unsupported Media Type');
|
|
}
|
|
|
|
store
|
|
.save(req.files.uploadimage)
|
|
.then(function (url) {
|
|
return res.send(url);
|
|
})
|
|
.otherwise(function (e) {
|
|
return errors.logError(e);
|
|
});
|
|
},
|
|
'login': function (req, res) {
|
|
/*jslint unparam:true*/
|
|
res.render('login', {
|
|
bodyClass: 'ghost-login',
|
|
hideNavbar: true,
|
|
adminNav: setSelected(adminNavbar, 'login')
|
|
});
|
|
},
|
|
'auth': function (req, res) {
|
|
var currentTime = process.hrtime()[0],
|
|
denied = '';
|
|
loginSecurity = _.filter(loginSecurity, function (ipTime) {
|
|
return (ipTime.time + 2 > currentTime);
|
|
});
|
|
denied = _.find(loginSecurity, function (ipTime) {
|
|
return (ipTime.ip === req.connection.remoteAddress);
|
|
});
|
|
|
|
if (!denied) {
|
|
loginSecurity.push({ip: req.connection.remoteAddress, time: process.hrtime()[0]});
|
|
api.users.check({email: req.body.email, pw: req.body.password}).then(function (user) {
|
|
req.session.regenerate(function (err) {
|
|
if (!err) {
|
|
req.session.user = user.id;
|
|
var redirect = config.paths().webroot + '/ghost/';
|
|
if (req.body.redirect) {
|
|
redirect += decodeURIComponent(req.body.redirect);
|
|
}
|
|
|
|
res.json(200, {redirect: redirect});
|
|
}
|
|
});
|
|
}, function (error) {
|
|
res.json(401, {error: error.message});
|
|
});
|
|
} else {
|
|
res.json(401, {error: 'Slow down, there are way too many login attempts!'});
|
|
}
|
|
},
|
|
'changepw': function (req, res) {
|
|
return api.users.changePassword({
|
|
currentUser: req.session.user,
|
|
oldpw: req.body.password,
|
|
newpw: req.body.newpassword,
|
|
ne2pw: req.body.ne2password
|
|
}).then(function () {
|
|
res.json(200, {msg: 'Password changed successfully'});
|
|
}, function (error) {
|
|
res.send(401, {error: error.message});
|
|
});
|
|
},
|
|
'signup': function (req, res) {
|
|
/*jslint unparam:true*/
|
|
res.render('signup', {
|
|
bodyClass: 'ghost-signup',
|
|
hideNavbar: true,
|
|
adminNav: setSelected(adminNavbar, 'login')
|
|
});
|
|
},
|
|
'doRegister': function (req, res) {
|
|
var name = req.body.name,
|
|
email = req.body.email,
|
|
password = req.body.password;
|
|
|
|
api.users.add({
|
|
name: name,
|
|
email: email,
|
|
password: password
|
|
}).then(function (user) {
|
|
api.settings.edit('email', email).then(function () {
|
|
req.session.regenerate(function (err) {
|
|
if (!err) {
|
|
if (req.session.user === undefined) {
|
|
req.session.user = user.id;
|
|
}
|
|
res.json(200, {redirect: config.paths().webroot + '/ghost/'});
|
|
}
|
|
});
|
|
});
|
|
}).otherwise(function (error) {
|
|
res.json(401, {error: error.message});
|
|
});
|
|
},
|
|
'forgotten': function (req, res) {
|
|
/*jslint unparam:true*/
|
|
res.render('forgotten', {
|
|
bodyClass: 'ghost-forgotten',
|
|
hideNavbar: true,
|
|
adminNav: setSelected(adminNavbar, 'login')
|
|
});
|
|
},
|
|
'generateResetToken': function (req, res) {
|
|
var email = req.body.email;
|
|
|
|
api.users.generateResetToken(email).then(function (token) {
|
|
var siteLink = '<a href="' + config().url + '">' + config().url + '</a>',
|
|
resetUrl = config().url.replace(/\/$/, '') + '/ghost/reset/' + token + '/',
|
|
resetLink = '<a href="' + resetUrl + '">' + resetUrl + '</a>',
|
|
message = {
|
|
to: email,
|
|
subject: 'Reset Password',
|
|
html: '<p><strong>Hello!</strong></p>' +
|
|
'<p>A request has been made to reset the password on the site ' + siteLink + '.</p>' +
|
|
'<p>Please follow the link below to reset your password:<br><br>' + resetLink + '</p>' +
|
|
'<p>Ghost</p>'
|
|
};
|
|
|
|
return mailer.send(message);
|
|
}).then(function success() {
|
|
var notification = {
|
|
type: 'success',
|
|
message: 'Check your email for further instructions',
|
|
status: 'passive',
|
|
id: 'successresetpw'
|
|
};
|
|
|
|
return api.notifications.add(notification).then(function () {
|
|
res.json(200, {redirect: config.paths().webroot + '/ghost/signin/'});
|
|
});
|
|
|
|
}, function failure(error) {
|
|
// TODO: This is kind of sketchy, depends on magic string error.message from Bookshelf.
|
|
// TODO: It's debatable whether we want to just tell the user we sent the email in this case or not, we are giving away sensitive info here.
|
|
if (error && error.message === 'EmptyResponse') {
|
|
error.message = "Invalid email address";
|
|
}
|
|
|
|
res.json(401, {error: error.message});
|
|
});
|
|
},
|
|
'reset': function (req, res) {
|
|
// Validate the request token
|
|
var token = req.params.token;
|
|
|
|
api.users.validateToken(token).then(function () {
|
|
// Render the reset form
|
|
res.render('reset', {
|
|
bodyClass: 'ghost-reset',
|
|
hideNavbar: true,
|
|
adminNav: setSelected(adminNavbar, 'reset')
|
|
});
|
|
}).otherwise(function (err) {
|
|
// Redirect to forgotten if invalid token
|
|
var notification = {
|
|
type: 'error',
|
|
message: 'Invalid or expired token',
|
|
status: 'persistent',
|
|
id: 'errorinvalidtoken'
|
|
};
|
|
|
|
errors.logError(err, 'admin.js', "Please check the provided token for validity and expiration.");
|
|
|
|
return api.notifications.add(notification).then(function () {
|
|
res.redirect(config.paths().webroot + '/ghost/forgotten');
|
|
});
|
|
});
|
|
},
|
|
'resetPassword': function (req, res) {
|
|
var token = req.params.token,
|
|
newPassword = req.param('newpassword'),
|
|
ne2Password = req.param('ne2password');
|
|
|
|
api.users.resetPassword(token, newPassword, ne2Password).then(function () {
|
|
var notification = {
|
|
type: 'success',
|
|
message: 'Password changed successfully.',
|
|
status: 'passive',
|
|
id: 'successresetpw'
|
|
};
|
|
|
|
return api.notifications.add(notification).then(function () {
|
|
res.json(200, {redirect: config.paths().webroot + '/ghost/signin/'});
|
|
});
|
|
}).otherwise(function (err) {
|
|
// TODO: Better error message if we can tell whether the passwords didn't match or something
|
|
res.json(401, {error: err.message});
|
|
});
|
|
},
|
|
'logout': function (req, res) {
|
|
req.session.destroy();
|
|
|
|
var notification = {
|
|
type: 'success',
|
|
message: 'You were successfully signed out',
|
|
status: 'passive',
|
|
id: 'successlogout'
|
|
};
|
|
|
|
return api.notifications.add(notification).then(function () {
|
|
res.redirect(config.paths().webroot + '/ghost/signin/');
|
|
});
|
|
},
|
|
'index': function (req, res) {
|
|
/*jslint unparam:true*/
|
|
res.render('content', {
|
|
bodyClass: 'manage',
|
|
adminNav: setSelected(adminNavbar, 'content')
|
|
});
|
|
},
|
|
'editor': function (req, res) {
|
|
if (req.params.id !== undefined) {
|
|
res.render('editor', {
|
|
bodyClass: 'editor',
|
|
adminNav: setSelected(adminNavbar, 'content')
|
|
});
|
|
} else {
|
|
res.render('editor', {
|
|
bodyClass: 'editor',
|
|
adminNav: setSelected(adminNavbar, 'add')
|
|
});
|
|
}
|
|
},
|
|
'content': function (req, res) {
|
|
/*jslint unparam:true*/
|
|
res.render('content', {
|
|
bodyClass: 'manage',
|
|
adminNav: setSelected(adminNavbar, 'content')
|
|
});
|
|
},
|
|
'settings': function (req, res, next) {
|
|
|
|
// TODO: Centralise list/enumeration of settings panes, so we don't
|
|
// run into trouble in future.
|
|
var allowedSections = ['', 'general', 'user'],
|
|
section = req.url.replace(/(^\/ghost\/settings[\/]*|\/$)/ig, '');
|
|
|
|
if (allowedSections.indexOf(section) < 0) {
|
|
return next();
|
|
}
|
|
|
|
res.render('settings', {
|
|
bodyClass: 'settings',
|
|
adminNav: setSelected(adminNavbar, 'settings')
|
|
});
|
|
},
|
|
'debug': { /* ugly temporary stuff for managing the app before it's properly finished */
|
|
index: function (req, res) {
|
|
/*jslint unparam:true*/
|
|
res.render('debug', {
|
|
bodyClass: 'settings',
|
|
adminNav: setSelected(adminNavbar, 'settings')
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
module.exports = adminControllers;
|