mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-04-15 03:01:37 -05:00
Added /unsubscribe/ route to the front-end (#11394)
no issue - adds new router to the frontend for handling unsubscribe - default template lives in `core/server/frontend/views/unsubscribe.hbs` - `{{error}}` is present and contains the error message when unsubscribe fails - `{{member}}` is present and contains the member email - updated unsubscribe url to match the new format
This commit is contained in:
parent
52eb3ca9da
commit
ee47dd4dae
7 changed files with 92 additions and 23 deletions
27
core/frontend/services/routing/UnsubscribeRouter.js
Normal file
27
core/frontend/services/routing/UnsubscribeRouter.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
const ParentRouter = require('./ParentRouter');
|
||||
const controllers = require('./controllers');
|
||||
|
||||
/**
|
||||
* @description Unsubscribe Router.
|
||||
*
|
||||
* "/unsubscribe/" -> Unsubscribe Router
|
||||
*/
|
||||
class UnsubscribeRouter extends ParentRouter {
|
||||
constructor() {
|
||||
super('UnsubscribeRouter');
|
||||
|
||||
// @NOTE: hardcoded, not configurable
|
||||
this.route = {value: '/unsubscribe/'};
|
||||
this._registerRoutes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Register all routes of this router.
|
||||
* @private
|
||||
*/
|
||||
_registerRoutes() {
|
||||
this.mountRoute(this.route.value, controllers.unsubscribe);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UnsubscribeRouter;
|
8
core/frontend/services/routing/bootstrap.js
vendored
8
core/frontend/services/routing/bootstrap.js
vendored
|
@ -9,6 +9,7 @@ const CollectionRouter = require('./CollectionRouter');
|
|||
const TaxonomyRouter = require('./TaxonomyRouter');
|
||||
const PreviewRouter = require('./PreviewRouter');
|
||||
const ParentRouter = require('./ParentRouter');
|
||||
const UnsubscribeRouter = require('./UnsubscribeRouter');
|
||||
|
||||
const registry = require('./registry');
|
||||
let siteRouter;
|
||||
|
@ -50,7 +51,7 @@ module.exports.init = (options = {start: false}) => {
|
|||
* The routers are created in a specific order. This order defines who can get a resource first or
|
||||
* who can dominant other routers.
|
||||
*
|
||||
* 1. Preview Router: Is the strongest and is an inbuilt feature, which you can never override.
|
||||
* 1. Preview + Unsubscribe Routers: Strongest inbuilt features, which you can never override.
|
||||
* 2. Static Routes: Very strong, because you can override any urls and redirect to a static route.
|
||||
* 3. Taxonomies: Stronger than collections, because it's an inbuilt feature.
|
||||
* 4. Collections
|
||||
|
@ -61,8 +62,11 @@ module.exports.start = () => {
|
|||
const apiVersion = themeService.getApiVersion();
|
||||
const RESOURCE_CONFIG = require(`./config/${apiVersion}`);
|
||||
|
||||
const previewRouter = new PreviewRouter(RESOURCE_CONFIG);
|
||||
const unsubscribeRouter = new UnsubscribeRouter();
|
||||
siteRouter.mountRouter(unsubscribeRouter.router());
|
||||
registry.setRouter('unsubscribeRouter', unsubscribeRouter);
|
||||
|
||||
const previewRouter = new PreviewRouter(RESOURCE_CONFIG);
|
||||
siteRouter.mountRouter(previewRouter.router());
|
||||
registry.setRouter('previewRouter', previewRouter);
|
||||
|
||||
|
|
|
@ -21,5 +21,9 @@ module.exports = {
|
|||
|
||||
get static() {
|
||||
return require('./static');
|
||||
},
|
||||
|
||||
get unsubscribe() {
|
||||
return require('./unsubscribe');
|
||||
}
|
||||
};
|
||||
|
|
31
core/frontend/services/routing/controllers/unsubscribe.js
Normal file
31
core/frontend/services/routing/controllers/unsubscribe.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
const debug = require('ghost-ignition').debug('services:routing:controllers:unsubscribe');
|
||||
const path = require('path');
|
||||
const megaService = require('../../../../server/services/mega');
|
||||
const labsService = require('../../../../server/services/labs');
|
||||
const helpers = require('../../../services/routing/helpers');
|
||||
|
||||
module.exports = async function unsubscribeController(req, res, next) {
|
||||
debug('unsubscribeController');
|
||||
|
||||
if (!labsService.isSet('members')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
let data = {};
|
||||
|
||||
try {
|
||||
data.member = await megaService.mega.handleUnsubscribeRequest(req);
|
||||
} catch (err) {
|
||||
data.error = err.message;
|
||||
}
|
||||
|
||||
const templateName = 'unsubscribe';
|
||||
|
||||
res.routerOptions = {
|
||||
type: 'custom',
|
||||
templates: templateName,
|
||||
defaultTemplate: path.resolve(__dirname, '../../../views/', templateName)
|
||||
};
|
||||
|
||||
return helpers.renderer(req, res, data);
|
||||
};
|
19
core/frontend/views/unsubscribe.hbs
Normal file
19
core/frontend/views/unsubscribe.hbs
Normal file
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>
|
||||
{{#if member}}Successfully Unsubscribed{{/if}}
|
||||
{{#if error}}Unsubscribe Failed{{/if}}
|
||||
</title>
|
||||
</head>
|
||||
<body>
|
||||
<p><a href="{{@site.url}}">Back to site</a></p>
|
||||
<p>
|
||||
{{#if member}}Success! {{member.email}} has been successfully unsubscribed.{{/if}}
|
||||
{{#if error}}Uh oh! Unsubscribe failed: "{{error}}"{{/if}}
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
|
@ -108,8 +108,8 @@ const serialize = async (model) => {
|
|||
function createUnsubscribeUrl(member) {
|
||||
const siteUrl = urlUtils.getSiteUrl();
|
||||
const unsubscribeUrl = new URL(siteUrl);
|
||||
unsubscribeUrl.searchParams.set('action', 'unsubscribe');
|
||||
unsubscribeUrl.searchParams.set('unsubscribe', member.uuid);
|
||||
unsubscribeUrl.pathname = `${unsubscribeUrl.pathname}/unsubscribe/`.replace('//', '/');
|
||||
unsubscribeUrl.searchParams.set('uuid', member.uuid);
|
||||
|
||||
return unsubscribeUrl.href;
|
||||
}
|
||||
|
@ -135,14 +135,14 @@ async function handleUnsubscribeRequest(req) {
|
|||
}
|
||||
|
||||
const {query} = url.parse(req.url, true);
|
||||
if (!query || !query.unsubscribe) {
|
||||
if (!query || !query.uuid) {
|
||||
throw new common.errors.BadRequestError({
|
||||
message: 'Expected unsubscribe param containing token'
|
||||
});
|
||||
}
|
||||
|
||||
const member = await membersService.api.members.get({
|
||||
uuid: query.unsubscribe
|
||||
uuid: query.uuid
|
||||
});
|
||||
|
||||
if (!member) {
|
||||
|
@ -152,7 +152,7 @@ async function handleUnsubscribeRequest(req) {
|
|||
}
|
||||
|
||||
try {
|
||||
await membersService.api.members.update({subscribed: false}, {id: member.id});
|
||||
return await membersService.api.members.update({subscribed: false}, {id: member.id});
|
||||
} catch (err) {
|
||||
throw new common.errors.InternalServerError({
|
||||
message: 'Failed to unsubscribe member'
|
||||
|
|
|
@ -15,7 +15,6 @@ const labsService = require('../../services/labs');
|
|||
const urlUtils = require('../../lib/url-utils');
|
||||
const sitemapHandler = require('../../../frontend/services/sitemap/handler');
|
||||
const themeMiddleware = require('../../../frontend/services/themes').middleware;
|
||||
const megaService = require('../../services/mega');
|
||||
const membersService = require('../../services/members');
|
||||
const siteRoutes = require('./routes');
|
||||
const shared = require('../shared');
|
||||
|
@ -190,21 +189,6 @@ module.exports = function setupSiteApp(options = {}) {
|
|||
return next();
|
||||
}
|
||||
});
|
||||
siteApp.use(async function (req, res, next) {
|
||||
if (!labsService.isSet('members')) {
|
||||
return next();
|
||||
}
|
||||
if (!req.url.includes('unsubscribe=')) {
|
||||
return next();
|
||||
}
|
||||
try {
|
||||
await megaService.mega.handleUnsubscribeRequest(req);
|
||||
next();
|
||||
} catch (err) {
|
||||
common.logging.warn(err.message);
|
||||
return next();
|
||||
}
|
||||
});
|
||||
siteApp.use(function (req, res, next) {
|
||||
res.locals.member = req.member;
|
||||
next();
|
||||
|
|
Loading…
Add table
Reference in a new issue