mirror of
https://github.com/wassname/talk.git
synced 2026-07-03 20:57:49 +08:00
Merge branch 'master' of github.com:coralproject/talk into moderate-bio
This commit is contained in:
@@ -5,6 +5,7 @@ const path = require('path');
|
||||
const helmet = require('helmet');
|
||||
const passport = require('./services/passport');
|
||||
const session = require('express-session');
|
||||
const enabled = require('debug').enabled;
|
||||
const RedisStore = require('connect-redis')(session);
|
||||
const redis = require('./services/redis');
|
||||
const csrf = require('csurf');
|
||||
@@ -120,7 +121,7 @@ app.use((req, res, next) => {
|
||||
// returning a status code that makes sense.
|
||||
app.use('/api', (err, req, res, next) => {
|
||||
if (err !== ErrNotFound) {
|
||||
if (app.get('env') !== 'test') {
|
||||
if (app.get('env') !== 'test' || enabled('talk:errors')) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ router.post('/password/reset', (req, res, next) => {
|
||||
token,
|
||||
rootURL: process.env.TALK_ROOT_URL
|
||||
},
|
||||
subject: 'Password Reset Requested - Talk',
|
||||
subject: 'Password Reset',
|
||||
to: email
|
||||
});
|
||||
})
|
||||
|
||||
+62
-17
@@ -80,6 +80,34 @@ router.post('/:user_id/email', authorization.needed('admin'), (req, res, next) =
|
||||
.catch(next);
|
||||
});
|
||||
|
||||
// /**
|
||||
// * SendEmailConfirmation sends a confirmation email to the user.
|
||||
// * @param {Request} req express request object
|
||||
// * @param {String} email user email address
|
||||
// */
|
||||
|
||||
/**
|
||||
* SendEmailConfirmation sends a confirmation email to the user.
|
||||
* @param {ExpressApp} app the instance of the express app
|
||||
* @param {String} userID the id for the user to send the email to
|
||||
* @param {String} email the email for the user to send the email to
|
||||
*/
|
||||
const SendEmailConfirmation = (app, userID, email) => User
|
||||
.createEmailConfirmToken(userID, email)
|
||||
.then((token) => {
|
||||
return mailer.sendSimple({
|
||||
app, // needed to render the templates.
|
||||
template: 'email/email-confirm', // needed to know which template to render!
|
||||
locals: { // specifies the template locals.
|
||||
token,
|
||||
rootURL: process.env.TALK_ROOT_URL,
|
||||
email
|
||||
},
|
||||
subject: 'Email Confirmation',
|
||||
to: email
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/', (req, res, next) => {
|
||||
const {
|
||||
email,
|
||||
@@ -98,23 +126,7 @@ router.post('/', (req, res, next) => {
|
||||
|
||||
if (requireEmailConfirmation) {
|
||||
|
||||
// Email confirmation is required, let's generate that token and send
|
||||
// the email.
|
||||
return User
|
||||
.createEmailConfirmToken(user.id, email)
|
||||
.then((token) => {
|
||||
return mailer.sendSimple({
|
||||
app: req.app, // needed to render the templates.
|
||||
template: 'email/email-confirm', // needed to know which template to render!
|
||||
locals: { // specifies the template locals.
|
||||
token,
|
||||
rootURL: process.env.TALK_ROOT_URL,
|
||||
email
|
||||
},
|
||||
subject: 'Email Confirmation - Talk',
|
||||
to: email
|
||||
});
|
||||
})
|
||||
SendEmailConfirmation(req.app, user.id, email)
|
||||
.then(() => {
|
||||
|
||||
// Then send back the user.
|
||||
@@ -158,4 +170,37 @@ router.post('/:user_id/actions', authorization.needed(), (req, res, next) => {
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/:user_id/email/confirm', authorization.needed('admin'), (req, res, next) => {
|
||||
const {
|
||||
user_id
|
||||
} = req.params;
|
||||
|
||||
User
|
||||
.findById(user_id)
|
||||
.then((user) => {
|
||||
if (!user) {
|
||||
res.status(404).end();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the first local profile.
|
||||
let localProfile = user.profiles.find((profile) => profile.provider === 'local');
|
||||
|
||||
// If there was no local profile for the user, error out.
|
||||
if (!localProfile) {
|
||||
res.status(404).end();
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the email to the first local profile that was found.
|
||||
return SendEmailConfirmation(req.app, user.id, localProfile.id)
|
||||
.then(() => {
|
||||
res.status(204).end();
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
next(err);
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
+57
-2
@@ -14,7 +14,7 @@ const Queue = module.exports.queue = kue.createQueue({
|
||||
}
|
||||
});
|
||||
|
||||
module.exports.Task = class Task {
|
||||
class Task {
|
||||
|
||||
constructor({name, attempts = 3, delay = 1000}) {
|
||||
this.name = name;
|
||||
@@ -76,4 +76,59 @@ module.exports.Task = class Task {
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the tasks during testing.
|
||||
* @type {Array}
|
||||
*/
|
||||
const TestQueue = [];
|
||||
|
||||
/**
|
||||
* TestTask is a Task queue that is implemented for when the application is in
|
||||
* test mode, and does not send the jobs to redis, instead it queues them in
|
||||
* an array which can be inspected.
|
||||
*/
|
||||
class TestTask {
|
||||
|
||||
constructor({name}) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push the task into the fake queue.
|
||||
*/
|
||||
create(task) {
|
||||
let id = TestQueue.push({
|
||||
name: this.name,
|
||||
task
|
||||
});
|
||||
|
||||
return Promise.resolve({id});
|
||||
}
|
||||
|
||||
// This is a NO-OP action simply provided to match the Task interface.
|
||||
process() { return null; }
|
||||
|
||||
/**
|
||||
* Returns the current tasks for this queue.
|
||||
* @return {Array} the tasks in the queue
|
||||
*/
|
||||
get tasks() {
|
||||
return TestQueue
|
||||
.filter((testTask) => testTask.name === this.name)
|
||||
.map((testTask) => testTask.task);
|
||||
}
|
||||
|
||||
static shutdown() {
|
||||
return Task.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
module.exports.Task = TestTask;
|
||||
module.exports.TestQueue = TestQueue;
|
||||
} else {
|
||||
module.exports.Task = Task;
|
||||
}
|
||||
|
||||
+5
-2
@@ -65,13 +65,16 @@ const mailer = module.exports = {
|
||||
return Promise.reject('sendSimple requires a subject for the email');
|
||||
}
|
||||
|
||||
// Prefix the subject with `[Talk]`.
|
||||
subject = `[Talk] ${subject}`;
|
||||
|
||||
return Promise.all([
|
||||
|
||||
// Render the HTML version of the email.
|
||||
mailer.render(app, template, locals),
|
||||
mailer.render(app, `${template}.ejs`, locals),
|
||||
|
||||
// Render the TEXT version of the email.
|
||||
mailer.render(app, `${template}.txt`, locals)
|
||||
mailer.render(app, `${template}.txt.ejs`, locals)
|
||||
])
|
||||
.then(([html, text]) => {
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
const kue = require('../services/kue');
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
// Empty the test tasks before finishing.
|
||||
kue.TestQueue.splice(0, kue.TestQueue.length);
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
const passport = require('../../../passport');
|
||||
|
||||
const app = require('../../../../app');
|
||||
const mailer = require('../../../../services/mailer');
|
||||
const chai = require('chai');
|
||||
const expect = chai.expect;
|
||||
|
||||
@@ -10,6 +11,39 @@ chai.use(require('chai-http'));
|
||||
|
||||
const User = require('../../../../models/user');
|
||||
|
||||
describe('/api/v1/users/:user_id/email/confirm', () => {
|
||||
|
||||
let mockUser;
|
||||
|
||||
beforeEach(() => User.createLocalUser('ana@gmail.com', '123', 'Ana').then((user) => {
|
||||
mockUser = user;
|
||||
}));
|
||||
|
||||
describe('#post', () => {
|
||||
it('should send an email when we hit the endpoint', () => {
|
||||
expect(mailer.task.tasks).to.have.length(0);
|
||||
|
||||
return chai.request(app)
|
||||
.post(`/api/v1/users/${mockUser.id}/email/confirm`)
|
||||
.set(passport.inject({roles: ['admin']}))
|
||||
.then((res) => {
|
||||
expect(res).to.have.status(204);
|
||||
expect(mailer.task.tasks).to.have.length(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should send a 404 on not matching a user', () => {
|
||||
return chai.request(app)
|
||||
.post(`/api/v1/users/${mockUser.id}/email/confirm`)
|
||||
.set(passport.inject({roles: ['admin']}))
|
||||
.then((res) => {
|
||||
expect(res).to.have.status(204);
|
||||
expect(mailer.task.tasks).to.have.length(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('/api/v1/users/:user_id/actions', () => {
|
||||
|
||||
const users = [{
|
||||
|
||||
Reference in New Issue
Block a user