-
Notifications
You must be signed in to change notification settings - Fork 14
/
index.js
199 lines (165 loc) · 6.66 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
'use strict';
const db = require('ep_etherpad-lite/node/db/DB');
const fs = require('fs').promises;
const settings = require('ep_etherpad-lite/node/utils/Settings');
// Remove cache for this procedure
db.db.dbSettings.cache = 0;
exports.registerRoute = (hookName, args, callback) => {
// Catching (un)subscribe addresses
const handle = async (req, res) => {
console.warn('HERE');
const {padId, action, actionId} = req.params;
const padURL = settings.ep_email_notifications.urlToPads + encodeURIComponent(padId);
const userIds = await db.get(`emailSubscription:${padId}`);
let foundInDb = false;
let timeDiffGood = false;
let email = 'your email';
const {pending = {}} = userIds || {};
for (const [user, userInfo] of Object.entries(pending)) {
// If we have Id int the Db, then we are good ot really unsubscribe the user
if (userInfo[`${action}Id`] !== actionId) continue;
console.debug('emailSubscription:', user, 'found in DB:', userInfo);
foundInDb = true;
email = user;
// Checking if the demand is not older than 24h
const timeDiff = Date.now() - userInfo.timestamp;
timeDiffGood = timeDiff < 1000 * 60 * 60 * 24;
if (!timeDiffGood) continue;
if (action === 'subscribe') {
// Subscription process
await Promise.all([
setAuthorEmail(userInfo, user),
setAuthorEmailRegistered(userIds, userInfo, user, padId),
]);
} else if (action === 'unsubscribe') {
// Unsubscription process
await Promise.all([
unsetAuthorEmail(userInfo, user),
unsetAuthorEmailRegistered(userIds, user, padId),
]);
}
}
// Create and send the output message
await sendContent(res, args, action, padId, padURL, {foundInDb, timeDiffGood, email});
// Take a moment to clean all obsolete pending data
await cleanPendingData(padId);
};
args.app.get('/p/:padId/:action(subscribe|unsubscribe)=:actionId([\\s\\S]{0,})',
(req, res, next) => handle(req, res).catch((err) => next(err || new Error(err))));
callback(); // Am I even called?
};
/**
* Database manipulation
*/
// Updates the database with the email record
const setAuthorEmail = async (userInfo, email) => {
await db.setSub(`globalAuthor:${userInfo.authorId}`, ['email'], email);
};
// Write email and padId to the database
const setAuthorEmailRegistered = async (userIds, userInfo, email, padId) => {
console.debug('setAuthorEmailRegistered: Initial userIds:', userIds);
const timestamp = new Date().getTime();
const registered = {
authorId: userInfo.authorId,
onStart: userInfo.onStart,
onEnd: userInfo.onEnd,
timestamp,
};
// add the registered values to the object
userIds[email] = registered;
// remove the pending data
delete userIds.pending[email];
// Write the modified datas back in the Db
console.warn('written to database');
await db.set(`emailSubscription:${padId}`, userIds); // stick it in the database
console.debug('setAuthorEmailRegistered: Modified userIds:', userIds);
};
// Updates the database by removing the email record for that AuthorId
const unsetAuthorEmail = async (userInfo, email) => {
const value = await db.get(`globalAuthor:${userInfo.authorId}`); // get the current value
if (value.email !== email) return;
// Remove the email option from the datas
delete value.email;
// Write the modified datas back in the Db
await db.set(`globalAuthor:${userInfo.authorId}`, value);
};
// Remove email, options and padId from the database
const unsetAuthorEmailRegistered = async (userIds, email, padId) => {
console.debug('unsetAuthorEmailRegistered: initial userIds:', userIds);
// remove the registered options from the object
delete userIds[email];
// remove the pending data
delete userIds.pending[email];
// Write the modified datas back in the Db
console.warn('written to database');
await db.set(`emailSubscription:${padId}`, userIds);
console.debug('unsetAuthorEmailRegistered: modified userIds:', userIds);
};
/**
* We take a moment to remove too old pending (un)subscription
*/
const cleanPendingData = async (padId) => {
let areDataModified = false;
const userIds = await db.get(`emailSubscription:${padId}`); // get the current value
console.debug('cleanPendingData: Initial userIds:', userIds);
const {pending = {}} = userIds || {};
for (const user of Object.keys(pending)) {
const timeDiff = new Date().getTime() - pending[user].timestamp;
const timeDiffGood = timeDiff < 1000 * 60 * 60 * 24;
if (timeDiffGood) continue;
delete pending[user];
areDataModified = true;
}
if (areDataModified) {
// Write the modified datas back in the Db
await db.set(`emailSubscription:${padId}`, userIds);
}
console.debug(
`cleanPendingData: Modified userIds: ${userIds} / ${areDataModified}`);
};
/**
* Create html output with the status of the process
*/
const sendContent = async (res, args, action, padId, padURL, resultDb) => {
console.debug(
'starting sendContent: args ->', action, ' / ', padId, ' / ', padURL, ' / ', resultDb);
let actionMsg;
if (action === 'subscribe') {
actionMsg = `Subscribing '${resultDb.email}' to pad ${padId}`;
} else {
actionMsg = `Unsubscribing '${resultDb.email}' from pad ${padId}`;
}
let msgCause, resultMsg, classResult;
if (resultDb.foundInDb && resultDb.timeDiffGood) {
// Pending data were found un Db and updated -> good
resultMsg = 'Success';
classResult = 'validationGood';
if (action === 'subscribe') {
msgCause = 'You will receive email when someone changes this pad.';
} else {
msgCause = "You won't receive anymore email when someone changes this pad.";
}
} else if (resultDb.foundInDb) {
// Pending data were found but older than a day -> fail
resultMsg = 'Too late!';
classResult = 'validationBad';
msgCause = 'You have max 24h to click the link in your confirmation email.';
} else {
// Pending data weren't found in Db -> fail
resultMsg = 'Fail';
classResult = 'validationBad';
msgCause = `We couldn't find any pending
${action === 'subscribe' ? 'subscription' : 'unsubscription'}<br />
in our system with this Id.<br />
Maybe you wait more than 24h before validating`;
}
args.content = await fs.readFile(`${__dirname}/templates/response.ejs`, 'utf-8');
args.content = args.content
.replace(/<%action%>/, actionMsg)
.replace(/<%classResult%>/, classResult)
.replace(/<%result%>/, resultMsg)
.replace(/<%explanation%>/, msgCause)
.replace(/<%padUrl%>/g, padURL);
res.contentType('text/html; charset=utf-8');
res.send(args.content); // Send it to the requester*/
};