mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-09-03 08:31:49 +02:00
feat: support for one-click unsubscribe from email clients (#7203)
* feat: sending notifs via ACP creates real notification re: #7202 * feat: basic integration for one-click unsubscription #7202 * feat: tests for #7202 + bugfix * feat: added and organized digest unsub tests closes #7202
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
var nconf = require('nconf');
|
||||
var winston = require('winston');
|
||||
var _ = require('lodash');
|
||||
var jwt = require('jsonwebtoken');
|
||||
|
||||
var user = require('../../user');
|
||||
var languages = require('../../languages');
|
||||
@@ -183,6 +186,52 @@ settingsController.get = function (req, res, callback) {
|
||||
], callback);
|
||||
};
|
||||
|
||||
settingsController.unsubscribe = function (req, res) {
|
||||
if (!req.params.token) {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
|
||||
jwt.verify(req.params.token, nconf.get('secret'), function (err, payload) {
|
||||
if (err) {
|
||||
return res.sendStatus(403);
|
||||
}
|
||||
|
||||
switch (payload.template) {
|
||||
case 'digest':
|
||||
async.parallel([
|
||||
async.apply(user.setSetting, payload.uid, 'dailyDigestFreq', 'off'),
|
||||
async.apply(user.updateDigestSetting, payload.uid, 'off'),
|
||||
], function (err) {
|
||||
if (err) {
|
||||
winston.error('[settings/unsubscribe] One-click unsubscribe failed with error: ' + err.message);
|
||||
return res.sendStatus(500);
|
||||
}
|
||||
|
||||
return res.sendStatus(200);
|
||||
});
|
||||
break;
|
||||
case 'notification':
|
||||
async.waterfall([
|
||||
async.apply(db.getObjectField, 'user:' + payload.uid + ':settings', 'notificationType_' + payload.type),
|
||||
(current, next) => {
|
||||
user.setSetting(payload.uid, 'notificationType_' + payload.type, (current === 'notificationemail' ? 'notification' : 'none'), next);
|
||||
},
|
||||
], function (err) {
|
||||
if (err) {
|
||||
winston.error('[settings/unsubscribe] One-click unsubscribe failed with error: ' + err.message);
|
||||
return res.sendStatus(500);
|
||||
}
|
||||
|
||||
return res.sendStatus(200);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
res.sendStatus(404);
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function getNotificationSettings(userData, callback) {
|
||||
var privilegedTypes = [];
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ var url = require('url');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var _ = require('lodash');
|
||||
var jwt = require('jsonwebtoken');
|
||||
|
||||
var User = require('./user');
|
||||
var Plugins = require('./plugins');
|
||||
@@ -216,6 +217,31 @@ Emailer.sendToEmail = function (template, email, language, params, callback) {
|
||||
'List-Unsubscribe': '<' + [nconf.get('url'), 'uid', params.uid, 'settings'].join('/') + '>',
|
||||
}, params.headers);
|
||||
|
||||
// Digests and notifications can be one-click unsubbed
|
||||
let payload = {
|
||||
template: template,
|
||||
uid: params.uid,
|
||||
};
|
||||
|
||||
switch (template) {
|
||||
case 'digest':
|
||||
payload = jwt.sign(payload, nconf.get('secret'), {
|
||||
expiresIn: '30d',
|
||||
});
|
||||
params.headers['List-Unsubscribe'] = '<' + [nconf.get('url'), 'email', 'unsubscribe', payload].join('/') + '>';
|
||||
params.headers['List-Unsubscribe-Post'] = 'List-Unsubscribe=One-Click';
|
||||
break;
|
||||
|
||||
case 'notification':
|
||||
payload.type = params.notification.type;
|
||||
payload = jwt.sign(payload, nconf.get('secret'), {
|
||||
expiresIn: '30d',
|
||||
});
|
||||
params.headers['List-Unsubscribe'] = '<' + [nconf.get('url'), 'email', 'unsubscribe', payload].join('/') + '>';
|
||||
params.headers['List-Unsubscribe-Post'] = 'List-Unsubscribe=One-Click';
|
||||
break;
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Plugins.fireHook('filter:email.params', {
|
||||
|
||||
@@ -35,6 +35,7 @@ function mainRoutes(app, middleware, controllers) {
|
||||
setupPageRoute(app, '/tos', middleware, [], controllers.termsOfUse);
|
||||
|
||||
app.post('/compose', middleware.applyCSRF, controllers.composer.post);
|
||||
app.post('/email/unsubscribe/:token', controllers.accounts.settings.unsubscribe);
|
||||
}
|
||||
|
||||
function modRoutes(app, middleware, controllers) {
|
||||
|
||||
@@ -14,6 +14,7 @@ var userDigest = require('../user/digest');
|
||||
var userEmail = require('../user/email');
|
||||
var logger = require('../logger');
|
||||
var events = require('../events');
|
||||
var notifications = require('../notifications');
|
||||
var emailer = require('../emailer');
|
||||
var db = require('../database');
|
||||
var analytics = require('../analytics');
|
||||
@@ -273,6 +274,30 @@ SocketAdmin.email.test = function (socket, data, callback) {
|
||||
}, callback);
|
||||
break;
|
||||
|
||||
case 'notification':
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
notifications.create({
|
||||
type: 'test',
|
||||
bodyShort: '[[admin-settings-email:testing]]',
|
||||
bodyLong: '[[admin-settings-email:testing.send-help]]',
|
||||
nid: 'uid:' + socket.uid + ':test',
|
||||
path: '/',
|
||||
from: socket.uid,
|
||||
}, next);
|
||||
},
|
||||
function (notifObj, next) {
|
||||
emailer.send('notification', socket.uid, {
|
||||
path: notifObj.path,
|
||||
subject: utils.stripHTMLTags(notifObj.subject || '[[notifications:new_notification]]'),
|
||||
intro: utils.stripHTMLTags(notifObj.bodyShort),
|
||||
body: notifObj.bodyLong || '',
|
||||
notification: notifObj,
|
||||
showUnsubscribe: true,
|
||||
}, next);
|
||||
},
|
||||
]);
|
||||
break;
|
||||
default:
|
||||
emailer.send(data.template, socket.uid, payload, callback);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user