mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-01 23:37:43 +03:00
parent
1472035137
commit
adc5b18fb7
@ -31,6 +31,10 @@ module.exports = {
|
||||
return shared.pipeline(require('./posts'), localUtils);
|
||||
},
|
||||
|
||||
get invites() {
|
||||
return shared.pipeline(require('./invites'), localUtils);
|
||||
},
|
||||
|
||||
get mail() {
|
||||
return shared.pipeline(require('./mail'), localUtils);
|
||||
},
|
||||
|
165
core/server/api/v2/invites.js
Normal file
165
core/server/api/v2/invites.js
Normal file
@ -0,0 +1,165 @@
|
||||
const Promise = require('bluebird');
|
||||
const common = require('../../lib/common');
|
||||
const security = require('../../lib/security');
|
||||
const mailService = require('../../services/mail');
|
||||
const urlService = require('../../services/url');
|
||||
const settingsCache = require('../../services/settings/cache');
|
||||
const models = require('../../models');
|
||||
const api = require('./index');
|
||||
const ALLOWED_INCLUDES = ['created_by', 'updated_by'];
|
||||
const UNSAFE_ATTRS = ['role_id'];
|
||||
|
||||
module.exports = {
|
||||
docName: 'invites',
|
||||
|
||||
browse: {
|
||||
options: [
|
||||
'include',
|
||||
'page',
|
||||
'limit',
|
||||
'fields',
|
||||
'filter',
|
||||
'order',
|
||||
'debug'
|
||||
],
|
||||
validation: {
|
||||
options: {
|
||||
include: ALLOWED_INCLUDES
|
||||
}
|
||||
},
|
||||
permissions: true,
|
||||
query(frame) {
|
||||
return models.Invite.findPage(frame.options);
|
||||
}
|
||||
},
|
||||
|
||||
read: {
|
||||
options: [
|
||||
'include'
|
||||
],
|
||||
data: [
|
||||
'id',
|
||||
'email'
|
||||
],
|
||||
validation: {
|
||||
options: {
|
||||
include: ALLOWED_INCLUDES
|
||||
}
|
||||
},
|
||||
permissions: true,
|
||||
query(frame) {
|
||||
return models.Invite.findOne(frame.data, frame.options)
|
||||
.then((model) => {
|
||||
if (!model) {
|
||||
return Promise.reject(new common.errors.NotFoundError({
|
||||
message: common.i18n.t('errors.api.invites.inviteNotFound')
|
||||
}));
|
||||
}
|
||||
|
||||
return model;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
destroy: {
|
||||
statusCode: 204,
|
||||
options: [
|
||||
'include',
|
||||
'id'
|
||||
],
|
||||
validation: {
|
||||
options: {
|
||||
include: ALLOWED_INCLUDES
|
||||
}
|
||||
},
|
||||
permissions: true,
|
||||
query(frame) {
|
||||
frame.options.require = true;
|
||||
|
||||
return models.Invite.destroy(frame.options)
|
||||
.return(null);
|
||||
}
|
||||
},
|
||||
|
||||
add: {
|
||||
statusCode: 201,
|
||||
options: [
|
||||
'include',
|
||||
'email'
|
||||
],
|
||||
validation: {
|
||||
options: {
|
||||
include: ALLOWED_INCLUDES
|
||||
},
|
||||
data: {
|
||||
role_id: {
|
||||
require: true
|
||||
},
|
||||
email: {
|
||||
require: true
|
||||
}
|
||||
}
|
||||
},
|
||||
permissions: {
|
||||
unsafeAttrs: UNSAFE_ATTRS
|
||||
},
|
||||
query(frame) {
|
||||
let invite;
|
||||
let emailData;
|
||||
|
||||
return models.Invite.add(frame.data.invites[0], frame.options)
|
||||
.then((_invite) => {
|
||||
invite = _invite;
|
||||
|
||||
const adminUrl = urlService.utils.urlFor('admin', true);
|
||||
|
||||
emailData = {
|
||||
blogName: settingsCache.get('title'),
|
||||
invitedByName: frame.user.get('name'),
|
||||
invitedByEmail: frame.user.get('email'),
|
||||
resetLink: urlService.utils.urlJoin(adminUrl, 'signup', security.url.encodeBase64(invite.get('token')), '/')
|
||||
};
|
||||
|
||||
return mailService.utils.generateContent({data: emailData, template: 'invite-user'});
|
||||
})
|
||||
.then((emailContent) => {
|
||||
const payload = {
|
||||
mail: [{
|
||||
message: {
|
||||
to: invite.get('email'),
|
||||
subject: common.i18n.t('common.api.users.mail.invitedByName', {
|
||||
invitedByName: emailData.invitedByName,
|
||||
blogName: emailData.blogName
|
||||
}),
|
||||
html: emailContent.html,
|
||||
text: emailContent.text
|
||||
},
|
||||
options: {}
|
||||
}]
|
||||
};
|
||||
|
||||
return api.mail.send(payload, {context: {internal: true}});
|
||||
})
|
||||
.then(() => {
|
||||
return models.Invite.edit({
|
||||
status: 'sent'
|
||||
}, Object.assign({id: invite.id}, frame.options));
|
||||
})
|
||||
.then((invite) => {
|
||||
return invite;
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err && err.errorType === 'EmailError') {
|
||||
const errorMessage = common.i18n.t('errors.api.invites.errorSendingEmail.error', {
|
||||
message: err.message
|
||||
});
|
||||
const helpText = common.i18n.t('errors.api.invites.errorSendingEmail.help');
|
||||
err.message = `${errorMessage} ${helpText}`;
|
||||
common.logging.warn(err.message);
|
||||
}
|
||||
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
@ -19,6 +19,10 @@ module.exports = {
|
||||
return require('./posts');
|
||||
},
|
||||
|
||||
get invites() {
|
||||
return require('./invites');
|
||||
},
|
||||
|
||||
get settings() {
|
||||
return require('./settings');
|
||||
},
|
||||
|
26
core/server/api/v2/utils/serializers/output/invites.js
Normal file
26
core/server/api/v2/utils/serializers/output/invites.js
Normal file
@ -0,0 +1,26 @@
|
||||
const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:invites');
|
||||
|
||||
module.exports = {
|
||||
all(models, apiConfig, frame) {
|
||||
debug('all');
|
||||
|
||||
if (!models) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (models.meta) {
|
||||
frame.response = {
|
||||
invites: models.data.map(model => model.toJSON(frame.options)),
|
||||
meta: models.meta
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
frame.response = {
|
||||
invites: [models.toJSON(frame.options)]
|
||||
};
|
||||
|
||||
debug(frame.response);
|
||||
}
|
||||
};
|
@ -3,6 +3,10 @@ module.exports = {
|
||||
return require('./posts');
|
||||
},
|
||||
|
||||
get invites() {
|
||||
return require('./invites');
|
||||
},
|
||||
|
||||
get settings() {
|
||||
return require('./settings');
|
||||
}
|
||||
|
16
core/server/api/v2/utils/validators/input/invites.js
Normal file
16
core/server/api/v2/utils/validators/input/invites.js
Normal file
@ -0,0 +1,16 @@
|
||||
const Promise = require('bluebird');
|
||||
const common = require('../../../../../lib/common');
|
||||
const models = require('../../../../../models');
|
||||
|
||||
module.exports = {
|
||||
add(apiConfig, frame) {
|
||||
return models.User.findOne({email: frame.data.invites[0].email}, frame.options)
|
||||
.then((user) => {
|
||||
if (user) {
|
||||
return Promise.reject(new common.errors.ValidationError({
|
||||
message: common.i18n.t('errors.api.users.userAlreadyRegistered')
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
@ -194,10 +194,10 @@ module.exports = function apiRoutes() {
|
||||
);
|
||||
|
||||
// ## Invites
|
||||
router.get('/invites', mw.authAdminAPI, api.http(api.invites.browse));
|
||||
router.get('/invites/:id', mw.authAdminAPI, api.http(api.invites.read));
|
||||
router.post('/invites', mw.authAdminAPI, api.http(api.invites.add));
|
||||
router.del('/invites/:id', mw.authAdminAPI, api.http(api.invites.destroy));
|
||||
router.get('/invites', mw.authAdminAPI, apiv2.http(apiv2.invites.browse));
|
||||
router.get('/invites/:id', mw.authAdminAPI, apiv2.http(apiv2.invites.read));
|
||||
router.post('/invites', mw.authAdminAPI, apiv2.http(apiv2.invites.add));
|
||||
router.del('/invites/:id', mw.authAdminAPI, apiv2.http(apiv2.invites.destroy));
|
||||
|
||||
// ## Redirects (JSON based)
|
||||
router.get('/redirects/json', mw.authAdminAPI, api.http(api.redirects.download));
|
||||
|
179
core/test/functional/api/v2/admin/invites_spec.js
Normal file
179
core/test/functional/api/v2/admin/invites_spec.js
Normal file
@ -0,0 +1,179 @@
|
||||
const should = require('should');
|
||||
const supertest = require('supertest');
|
||||
const sinon = require('sinon');
|
||||
const testUtils = require('../../../../utils');
|
||||
const localUtils = require('./utils');
|
||||
const config = require('../../../../../../core/server/config');
|
||||
const mailService = require('../../../../../../core/server/services/mail');
|
||||
const ghost = testUtils.startGhost;
|
||||
const sandbox = sinon.sandbox.create();
|
||||
let request;
|
||||
|
||||
describe('Invites API V2', function () {
|
||||
var accesstoken = '', ghostServer;
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
return localUtils.doAuth(request, 'invites');
|
||||
})
|
||||
.then(function (token) {
|
||||
accesstoken = token;
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox.stub(mailService.GhostMailer.prototype, 'send').resolves('Mail is disabled');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('browse', function () {
|
||||
it('default', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('invites/'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.invites);
|
||||
jsonResponse.invites.should.have.length(2);
|
||||
|
||||
testUtils.API.checkResponse(jsonResponse, 'invites');
|
||||
testUtils.API.checkResponse(jsonResponse.invites[0], 'invite');
|
||||
|
||||
jsonResponse.invites[0].status.should.eql('sent');
|
||||
jsonResponse.invites[0].email.should.eql('test1@ghost.org');
|
||||
jsonResponse.invites[0].role_id.should.eql(testUtils.roles.ids.admin);
|
||||
|
||||
jsonResponse.invites[1].status.should.eql('sent');
|
||||
jsonResponse.invites[1].email.should.eql('test2@ghost.org');
|
||||
jsonResponse.invites[1].role_id.should.eql(testUtils.roles.ids.author);
|
||||
|
||||
mailService.GhostMailer.prototype.send.called.should.be.false();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('read', function () {
|
||||
it('default', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`invites/${testUtils.DataGenerator.forKnex.invites[0].id}/`))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.invites);
|
||||
jsonResponse.invites.should.have.length(1);
|
||||
|
||||
testUtils.API.checkResponse(jsonResponse.invites[0], 'invite');
|
||||
|
||||
mailService.GhostMailer.prototype.send.called.should.be.false();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('add', function () {
|
||||
it('default', function (done) {
|
||||
request
|
||||
.post(localUtils.API.getApiQuery('invites/'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
invites: [{email: 'test@example.com', role_id: testUtils.existingData.roles[1].id}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(201)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.invites);
|
||||
jsonResponse.invites.should.have.length(1);
|
||||
|
||||
testUtils.API.checkResponse(jsonResponse.invites[0], 'invite');
|
||||
jsonResponse.invites[0].role_id.should.eql(testUtils.existingData.roles[1].id);
|
||||
|
||||
mailService.GhostMailer.prototype.send.called.should.be.true();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('no email', function () {
|
||||
return request
|
||||
.post(localUtils.API.getApiQuery('invites/'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
invites: [{role_id: testUtils.existingData.roles[1].id}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(422);
|
||||
});
|
||||
|
||||
it('user exists', function (done) {
|
||||
request.post(localUtils.API.getApiQuery('invites/'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
invites: [{email: 'ghost-author@example.com', role_id: testUtils.existingData.roles[1].id}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(422)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
mailService.GhostMailer.prototype.send.called.should.be.false();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('destroy', function () {
|
||||
it('default', function (done) {
|
||||
request.del(localUtils.API.getApiQuery(`invites/${testUtils.DataGenerator.forKnex.invites[0].id}/`))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(204)
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
mailService.GhostMailer.prototype.send.called.should.be.false();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user