2013-06-25 15:43:15 +04:00
|
|
|
var Ghost = require('../../ghost'),
|
2013-07-11 23:02:18 +04:00
|
|
|
dataExport = require('../data/export'),
|
|
|
|
dataImport = require('../data/import'),
|
2013-06-25 15:43:15 +04:00
|
|
|
_ = require('underscore'),
|
2013-08-05 21:31:29 +04:00
|
|
|
fs = require('fs-extra'),
|
2013-06-25 15:43:15 +04:00
|
|
|
path = require('path'),
|
|
|
|
when = require('when'),
|
|
|
|
nodefn = require('when/node/function'),
|
2013-07-11 23:02:18 +04:00
|
|
|
api = require('../api'),
|
2013-08-05 21:31:29 +04:00
|
|
|
moment = require('moment'),
|
2013-08-06 23:27:56 +04:00
|
|
|
errors = require('../errorHandling'),
|
2013-05-11 20:44:25 +04:00
|
|
|
|
2013-06-25 15:43:15 +04:00
|
|
|
ghost = new Ghost(),
|
|
|
|
dataProvider = ghost.dataProvider,
|
|
|
|
adminNavbar,
|
2013-08-22 23:48:36 +04:00
|
|
|
adminControllers,
|
|
|
|
loginSecurity = [];
|
2013-05-11 20:44:25 +04:00
|
|
|
|
2013-06-25 15:43:15 +04:00
|
|
|
// TODO: combine path/navClass to single "slug(?)" variable with no prefix
|
|
|
|
adminNavbar = {
|
|
|
|
dashboard: {
|
|
|
|
name: 'Dashboard',
|
|
|
|
navClass: 'dashboard',
|
|
|
|
key: 'admin.navbar.dashboard',
|
|
|
|
path: '/'
|
|
|
|
},
|
|
|
|
content: {
|
|
|
|
name: 'Content',
|
|
|
|
navClass: 'content',
|
|
|
|
key: 'admin.navbar.content',
|
|
|
|
path: '/content/'
|
|
|
|
},
|
|
|
|
add: {
|
|
|
|
name: 'New Post',
|
|
|
|
navClass: 'editor',
|
|
|
|
key: 'admin.navbar.editor',
|
|
|
|
path: '/editor/'
|
|
|
|
},
|
|
|
|
settings: {
|
|
|
|
name: 'Settings',
|
|
|
|
navClass: 'settings',
|
|
|
|
key: 'admin.navbar.settings',
|
|
|
|
path: '/settings/'
|
|
|
|
}
|
|
|
|
};
|
2013-05-11 20:44:25 +04:00
|
|
|
|
2013-08-22 23:48:36 +04:00
|
|
|
|
2013-06-25 20:58:26 +04:00
|
|
|
// TODO: make this a util or helper
|
2013-06-25 15:43:15 +04:00
|
|
|
function setSelected(list, name) {
|
|
|
|
_.each(list, function (item, key) {
|
|
|
|
item.selected = key === name;
|
2013-05-29 04:10:39 +04:00
|
|
|
});
|
2013-06-25 15:43:15 +04:00
|
|
|
return list;
|
|
|
|
}
|
2013-05-29 04:10:39 +04:00
|
|
|
|
2013-09-08 00:55:01 +04:00
|
|
|
// TODO: this could be a separate module
|
|
|
|
function getUniqueFileName(dir, name, ext, i, done) {
|
|
|
|
var filename,
|
|
|
|
append = '';
|
|
|
|
|
|
|
|
if (i) {
|
|
|
|
append = '-' + i;
|
|
|
|
}
|
|
|
|
|
|
|
|
filename = path.join(dir, name + append + ext);
|
|
|
|
fs.exists(filename, function (exists) {
|
|
|
|
if (exists) {
|
|
|
|
setImmediate(function () {
|
|
|
|
i = i + 1;
|
|
|
|
return getUniqueFileName(dir, name, ext, i, done);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
return done(filename);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-06-25 15:43:15 +04:00
|
|
|
adminControllers = {
|
2013-08-05 21:31:29 +04:00
|
|
|
'uploader': function (req, res) {
|
2013-09-08 00:55:01 +04:00
|
|
|
|
2013-08-05 21:31:29 +04:00
|
|
|
var currentDate = moment(),
|
|
|
|
month = currentDate.format("MMM"),
|
|
|
|
year = currentDate.format("YYYY"),
|
|
|
|
tmp_path = req.files.uploadimage.path,
|
|
|
|
dir = path.join('content/images', year, month),
|
2013-08-14 00:04:07 +04:00
|
|
|
ext = path.extname(req.files.uploadimage.name).toLowerCase(),
|
2013-09-08 00:55:01 +04:00
|
|
|
basename = path.basename(req.files.uploadimage.name, ext);
|
2013-08-05 21:31:29 +04:00
|
|
|
|
2013-09-08 00:55:01 +04:00
|
|
|
function renameFile(target_path) {
|
2013-08-05 21:31:29 +04:00
|
|
|
// adds directories recursively
|
|
|
|
fs.mkdirs(dir, function (err) {
|
|
|
|
if (err) {
|
2013-08-06 23:27:56 +04:00
|
|
|
errors.logError(err);
|
2013-08-05 21:31:29 +04:00
|
|
|
} else {
|
|
|
|
fs.copy(tmp_path, target_path, function (err) {
|
|
|
|
if (err) {
|
2013-08-06 23:27:56 +04:00
|
|
|
errors.logError(err);
|
2013-08-05 21:31:29 +04:00
|
|
|
} else {
|
2013-09-08 00:55:01 +04:00
|
|
|
// TODO: should delete temp file at tmp_path. Or just move the file instead of copy.
|
|
|
|
// the src for the image must be in URI format, not a file system path, which in Windows uses \
|
|
|
|
var src = path.join('/', target_path).replace(new RegExp('\\' + path.sep, 'g'), '/');
|
2013-08-05 21:31:29 +04:00
|
|
|
res.send(src);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-09-08 00:55:01 +04:00
|
|
|
// TODO: is it better to use file type eg. image/png?
|
2013-08-05 21:31:29 +04:00
|
|
|
if (ext === ".jpg" || ext === ".png" || ext === ".gif") {
|
2013-09-08 00:55:01 +04:00
|
|
|
getUniqueFileName(dir, basename, ext, null, function (filename) {
|
|
|
|
renameFile(filename);
|
|
|
|
});
|
2013-08-05 21:31:29 +04:00
|
|
|
} else {
|
2013-08-27 22:54:06 +04:00
|
|
|
res.send(404, "Invalid filetype");
|
2013-08-05 21:31:29 +04:00
|
|
|
}
|
|
|
|
},
|
2013-06-25 15:43:15 +04:00
|
|
|
'login': function (req, res) {
|
2013-09-01 02:20:12 +04:00
|
|
|
res.render('signup', {
|
2013-06-25 15:43:15 +04:00
|
|
|
bodyClass: 'ghost-login',
|
|
|
|
hideNavbar: true,
|
|
|
|
adminNav: setSelected(adminNavbar, 'login')
|
2013-05-11 20:44:25 +04:00
|
|
|
});
|
2013-06-25 15:43:15 +04:00
|
|
|
},
|
|
|
|
'auth': function (req, res) {
|
2013-08-22 23:48:36 +04:00
|
|
|
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);
|
2013-06-25 15:43:15 +04:00
|
|
|
});
|
2013-08-22 23:48:36 +04:00
|
|
|
|
|
|
|
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.user = user.id;
|
|
|
|
res.json(200, {redirect: req.body.redirect ? '/ghost/'
|
|
|
|
+ decodeURIComponent(req.body.redirect) : '/ghost/'});
|
|
|
|
}, function (error) {
|
|
|
|
res.json(401, {error: error.message});
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
res.json(401, {error: 'Slow down, there are way too many login attempts!'});
|
|
|
|
}
|
|
|
|
|
2013-06-25 15:43:15 +04:00
|
|
|
},
|
2013-08-06 03:49:06 +04:00
|
|
|
changepw: function (req, res) {
|
|
|
|
api.users.changePassword({
|
2013-08-09 05:22:49 +04:00
|
|
|
currentUser: req.session.user,
|
2013-08-06 03:49:06 +04:00
|
|
|
oldpw: req.body.password,
|
|
|
|
newpw: req.body.newpassword,
|
|
|
|
ne2pw: req.body.ne2password
|
2013-08-20 22:52:44 +04:00
|
|
|
}).then(function () {
|
2013-08-06 03:49:06 +04:00
|
|
|
res.json(200, {msg: 'Password changed successfully'});
|
|
|
|
}, function (error) {
|
2013-08-20 22:52:44 +04:00
|
|
|
res.send(401, {error: error.message});
|
2013-08-06 03:49:06 +04:00
|
|
|
});
|
|
|
|
|
|
|
|
},
|
2013-06-25 15:43:15 +04:00
|
|
|
'signup': function (req, res) {
|
|
|
|
res.render('signup', {
|
|
|
|
bodyClass: 'ghost-login',
|
|
|
|
hideNavbar: true,
|
|
|
|
adminNav: setSelected(adminNavbar, 'login')
|
|
|
|
});
|
|
|
|
},
|
2013-09-01 02:20:12 +04:00
|
|
|
|
2013-06-25 15:43:15 +04:00
|
|
|
'doRegister': function (req, res) {
|
2013-09-08 23:16:40 +04:00
|
|
|
var name = req.body.name,
|
|
|
|
email = req.body.email,
|
2013-06-25 15:43:15 +04:00
|
|
|
password = req.body.password;
|
2013-05-11 20:44:25 +04:00
|
|
|
|
2013-08-19 01:50:42 +04:00
|
|
|
api.users.add({
|
2013-09-08 23:16:40 +04:00
|
|
|
full_name: name,
|
2013-08-19 01:50:42 +04:00
|
|
|
email_address: email,
|
|
|
|
password: password
|
|
|
|
}).then(function (user) {
|
2013-09-01 02:20:12 +04:00
|
|
|
|
2013-08-19 01:50:42 +04:00
|
|
|
if (req.session.user === undefined) {
|
|
|
|
req.session.user = user.id;
|
|
|
|
}
|
|
|
|
res.json(200, {redirect: '/ghost/'});
|
|
|
|
}, function (error) {
|
|
|
|
res.json(401, {error: error.message});
|
|
|
|
});
|
|
|
|
|
2013-06-25 15:43:15 +04:00
|
|
|
},
|
2013-09-01 02:20:12 +04:00
|
|
|
|
|
|
|
'forgotten': function (req, res) {
|
|
|
|
res.render('signup', {
|
|
|
|
bodyClass: 'ghost-forgotten',
|
|
|
|
hideNavbar: true,
|
|
|
|
adminNav: setSelected(adminNavbar, 'login')
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
'resetPassword': function (req, res) {
|
|
|
|
var email = req.body.email;
|
|
|
|
|
|
|
|
api.users.forgottenPassword(email).then(function (user) {
|
|
|
|
var message = {
|
|
|
|
to: email,
|
|
|
|
subject: 'Your new password',
|
|
|
|
html: "<p><strong>Hello!</strong></p>" +
|
2013-09-09 14:51:12 +04:00
|
|
|
"<p>You've reset your password. Here's the new one: " + user.newPassword + "</p>" +
|
|
|
|
"<p>Ghost <br/>" +
|
|
|
|
'<a href="' + ghost.config().env[process.env.NODE_ENV].url + '">' +
|
|
|
|
ghost.config().env[process.env.NODE_ENV].url + '</a></p>'
|
2013-09-01 02:20:12 +04:00
|
|
|
};
|
|
|
|
|
2013-09-04 17:57:41 +04:00
|
|
|
return ghost.mail.send(message);
|
|
|
|
}).then(function success() {
|
|
|
|
var notification = {
|
|
|
|
type: 'success',
|
|
|
|
message: 'Your password was changed successfully. Check your email for details.',
|
|
|
|
status: 'passive',
|
|
|
|
id: 'successresetpw'
|
|
|
|
};
|
|
|
|
|
|
|
|
return api.notifications.add(notification).then(function () {
|
|
|
|
res.json(200, {redirect: '/ghost/signin/'});
|
|
|
|
});
|
2013-09-01 02:20:12 +04:00
|
|
|
|
2013-09-04 17:57:41 +04:00
|
|
|
}, function failure(error) {
|
2013-09-01 02:20:12 +04:00
|
|
|
res.json(401, {error: error.message});
|
2013-09-04 17:57:41 +04:00
|
|
|
}).otherwise(errors.logAndThrowError);
|
2013-09-01 02:20:12 +04:00
|
|
|
},
|
2013-06-25 15:43:15 +04:00
|
|
|
'logout': function (req, res) {
|
|
|
|
delete req.session.user;
|
2013-09-04 17:57:41 +04:00
|
|
|
var notification = {
|
2013-08-20 03:40:09 +04:00
|
|
|
type: 'success',
|
2013-08-24 21:35:31 +04:00
|
|
|
message: 'You were successfully signed out',
|
2013-08-20 03:40:09 +04:00
|
|
|
status: 'passive',
|
|
|
|
id: 'successlogout'
|
|
|
|
};
|
|
|
|
|
2013-09-04 17:57:41 +04:00
|
|
|
return api.notifications.add(notification).then(function () {
|
|
|
|
res.redirect('/ghost/signin/');
|
|
|
|
});
|
2013-06-25 15:43:15 +04:00
|
|
|
},
|
|
|
|
'index': function (req, res) {
|
|
|
|
res.render('dashboard', {
|
|
|
|
bodyClass: 'dashboard',
|
|
|
|
adminNav: setSelected(adminNavbar, 'dashboard')
|
|
|
|
});
|
|
|
|
},
|
|
|
|
'editor': function (req, res) {
|
|
|
|
if (req.params.id !== undefined) {
|
2013-09-05 00:05:36 +04:00
|
|
|
res.render('editor', {
|
|
|
|
bodyClass: 'editor',
|
|
|
|
adminNav: setSelected(adminNavbar, 'content')
|
|
|
|
});
|
2013-06-25 15:43:15 +04:00
|
|
|
} else {
|
|
|
|
res.render('editor', {
|
|
|
|
bodyClass: 'editor',
|
|
|
|
adminNav: setSelected(adminNavbar, 'add')
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'content': function (req, res) {
|
2013-09-05 00:05:36 +04:00
|
|
|
res.render('content', {
|
|
|
|
bodyClass: 'manage',
|
|
|
|
adminNav: setSelected(adminNavbar, 'content')
|
|
|
|
});
|
2013-06-25 15:43:15 +04:00
|
|
|
},
|
|
|
|
'settings': function (req, res) {
|
2013-09-05 00:05:36 +04:00
|
|
|
res.render('settings', {
|
|
|
|
bodyClass: 'settings',
|
|
|
|
adminNav: setSelected(adminNavbar, 'settings')
|
|
|
|
});
|
2013-06-25 15:43:15 +04:00
|
|
|
},
|
|
|
|
'debug': { /* ugly temporary stuff for managing the app before it's properly finished */
|
|
|
|
index: function (req, res) {
|
|
|
|
res.render('debug', {
|
|
|
|
bodyClass: 'settings',
|
|
|
|
adminNav: setSelected(adminNavbar, 'settings')
|
|
|
|
});
|
|
|
|
},
|
|
|
|
'export': function (req, res) {
|
|
|
|
// Get current version from settings
|
|
|
|
api.settings.read({ key: "currentVersion" })
|
|
|
|
.then(function (setting) {
|
|
|
|
// Export the current versions data
|
|
|
|
return dataExport(setting.value);
|
|
|
|
}, function () {
|
|
|
|
// If no setting, assume 001
|
|
|
|
return dataExport("001");
|
|
|
|
})
|
|
|
|
.then(function (exportedData) {
|
|
|
|
// Save the exported data to the file system for download
|
2013-07-22 18:10:17 +04:00
|
|
|
var fileName = path.resolve(__dirname + '/../../server/data/export/exported-' + (new Date().getTime()) + '.json');
|
2013-06-24 00:55:03 +04:00
|
|
|
|
2013-06-25 15:43:15 +04:00
|
|
|
return nodefn.call(fs.writeFile, fileName, JSON.stringify(exportedData)).then(function () {
|
|
|
|
return when(fileName);
|
2013-06-24 00:55:03 +04:00
|
|
|
});
|
2013-06-25 15:43:15 +04:00
|
|
|
})
|
|
|
|
.then(function (exportedFilePath) {
|
|
|
|
// Send the exported data file
|
|
|
|
res.download(exportedFilePath, 'GhostData.json');
|
|
|
|
})
|
|
|
|
.otherwise(function (error) {
|
2013-06-24 00:55:03 +04:00
|
|
|
// Notify of an error if it occurs
|
2013-08-03 19:11:16 +04:00
|
|
|
var notification = {
|
|
|
|
type: 'error',
|
|
|
|
message: error.message || error,
|
|
|
|
status: 'persistent',
|
|
|
|
id: 'per-' + (ghost.notifications.length + 1)
|
|
|
|
};
|
|
|
|
|
|
|
|
return api.notifications.add(notification).then(function () {
|
|
|
|
res.redirect("/ghost/debug/");
|
|
|
|
});
|
2013-06-25 15:43:15 +04:00
|
|
|
});
|
|
|
|
},
|
|
|
|
'import': function (req, res) {
|
|
|
|
if (!req.files.importfile) {
|
|
|
|
// Notify of an error if it occurs
|
2013-08-03 19:11:16 +04:00
|
|
|
var notification = {
|
|
|
|
type: 'error',
|
|
|
|
message: "Must select a file to import",
|
|
|
|
status: 'persistent',
|
|
|
|
id: 'per-' + (ghost.notifications.length + 1)
|
|
|
|
};
|
|
|
|
|
|
|
|
return api.notifications.add(notification).then(function () {
|
|
|
|
res.redirect("/ghost/debug/");
|
|
|
|
});
|
2013-06-25 15:43:15 +04:00
|
|
|
}
|
2013-06-24 00:55:03 +04:00
|
|
|
|
2013-06-25 15:43:15 +04:00
|
|
|
// Get the current version for importing
|
|
|
|
api.settings.read({ key: "currentVersion" })
|
|
|
|
.then(function (setting) {
|
|
|
|
return when(setting.value);
|
|
|
|
}, function () {
|
|
|
|
return when("001");
|
|
|
|
})
|
|
|
|
.then(function (currentVersion) {
|
|
|
|
// Read the file contents
|
|
|
|
return nodefn.call(fs.readFile, req.files.importfile.path)
|
|
|
|
.then(function (fileContents) {
|
|
|
|
var importData;
|
2013-06-24 00:55:03 +04:00
|
|
|
|
2013-06-25 15:43:15 +04:00
|
|
|
// Parse the json data
|
|
|
|
try {
|
|
|
|
importData = JSON.parse(fileContents);
|
|
|
|
} catch (e) {
|
|
|
|
return when.reject(new Error("Failed to parse the import file"));
|
|
|
|
}
|
2013-06-24 00:55:03 +04:00
|
|
|
|
2013-06-25 15:43:15 +04:00
|
|
|
if (!importData.meta || !importData.meta.version) {
|
|
|
|
return when.reject(new Error("Import data does not specify version"));
|
|
|
|
}
|
2013-06-24 00:55:03 +04:00
|
|
|
|
2013-06-25 15:43:15 +04:00
|
|
|
// Import for the current version
|
|
|
|
return dataImport(currentVersion, importData);
|
|
|
|
});
|
|
|
|
})
|
2013-08-03 19:11:16 +04:00
|
|
|
.then(function importSuccess() {
|
|
|
|
var notification = {
|
|
|
|
type: 'success',
|
|
|
|
message: "Data imported. Log in with the user details you imported",
|
|
|
|
status: 'persistent',
|
|
|
|
id: 'per-' + (ghost.notifications.length + 1)
|
|
|
|
};
|
|
|
|
|
|
|
|
return api.notifications.add(notification).then(function () {
|
|
|
|
delete req.session.user;
|
2013-08-24 21:35:31 +04:00
|
|
|
res.redirect('/ghost/signin/');
|
2013-08-03 19:11:16 +04:00
|
|
|
});
|
|
|
|
|
|
|
|
}, function importFailure(error) {
|
2013-06-25 15:43:15 +04:00
|
|
|
// Notify of an error if it occurs
|
2013-08-03 19:11:16 +04:00
|
|
|
var notification = {
|
|
|
|
type: 'error',
|
|
|
|
message: error.message || error,
|
|
|
|
status: 'persistent',
|
|
|
|
id: 'per-' + (ghost.notifications.length + 1)
|
|
|
|
};
|
|
|
|
|
|
|
|
return api.notifications.add(notification).then(function () {
|
|
|
|
res.redirect('/ghost/debug/');
|
|
|
|
});
|
2013-06-25 15:43:15 +04:00
|
|
|
});
|
|
|
|
},
|
|
|
|
'reset': function (req, res) {
|
|
|
|
// Grab the current version so we can get the migration
|
2013-08-03 19:11:16 +04:00
|
|
|
dataProvider.reset()
|
|
|
|
.then(function resetSuccess() {
|
|
|
|
var notification = {
|
|
|
|
type: 'success',
|
|
|
|
message: "Database reset. Create a new user",
|
|
|
|
status: 'persistent',
|
|
|
|
id: 'per-' + (ghost.notifications.length + 1)
|
|
|
|
};
|
2013-06-24 00:55:03 +04:00
|
|
|
|
2013-08-03 19:11:16 +04:00
|
|
|
return api.notifications.add(notification).then(function () {
|
|
|
|
delete req.session.user;
|
|
|
|
res.redirect('/ghost/signup/');
|
|
|
|
});
|
|
|
|
}, function resetFailure(error) {
|
|
|
|
var notification = {
|
|
|
|
type: 'error',
|
|
|
|
message: error.message || error,
|
|
|
|
status: 'persistent',
|
|
|
|
id: 'per-' + (ghost.notifications.length + 1)
|
|
|
|
};
|
2013-06-24 00:55:03 +04:00
|
|
|
|
2013-08-03 19:11:16 +04:00
|
|
|
return api.notifications.add(notification).then(function () {
|
|
|
|
res.redirect('/ghost/debug/');
|
|
|
|
});
|
2013-06-25 15:43:15 +04:00
|
|
|
});
|
2013-05-11 20:44:25 +04:00
|
|
|
}
|
2013-06-25 15:43:15 +04:00
|
|
|
}
|
|
|
|
};
|
2013-05-11 20:44:25 +04:00
|
|
|
|
2013-08-09 05:22:49 +04:00
|
|
|
module.exports = adminControllers;
|