diff options
Diffstat (limited to 'server/lib/emailer.ts')
-rw-r--r-- | server/lib/emailer.ts | 123 |
1 files changed, 70 insertions, 53 deletions
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts index 7484524a4..d0874ab20 100644 --- a/server/lib/emailer.ts +++ b/server/lib/emailer.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { createTransport, Transporter } from 'nodemailer' | 1 | import { createTransport, Transporter } from 'nodemailer' |
2 | import { isTestInstance } from '../helpers/core-utils' | 2 | import { isTestInstance } from '../helpers/core-utils' |
3 | import { bunyanLogger, logger } from '../helpers/logger' | 3 | import { bunyanLogger, logger } from '../helpers/logger' |
4 | import { CONFIG } from '../initializers/config' | 4 | import { CONFIG, isEmailEnabled } from '../initializers/config' |
5 | import { JobQueue } from './job-queue' | 5 | import { JobQueue } from './job-queue' |
6 | import { EmailPayload } from './job-queue/handlers/email' | 6 | import { EmailPayload } from './job-queue/handlers/email' |
7 | import { readFileSync } from 'fs-extra' | 7 | import { readFileSync } from 'fs-extra' |
@@ -32,14 +32,15 @@ class Emailer { | |||
32 | private initialized = false | 32 | private initialized = false |
33 | private transporter: Transporter | 33 | private transporter: Transporter |
34 | 34 | ||
35 | private constructor () {} | 35 | private constructor () { |
36 | } | ||
36 | 37 | ||
37 | init () { | 38 | init () { |
38 | // Already initialized | 39 | // Already initialized |
39 | if (this.initialized === true) return | 40 | if (this.initialized === true) return |
40 | this.initialized = true | 41 | this.initialized = true |
41 | 42 | ||
42 | if (Emailer.isEnabled()) { | 43 | if (isEmailEnabled) { |
43 | logger.info('Using %s:%s as SMTP server.', CONFIG.SMTP.HOSTNAME, CONFIG.SMTP.PORT) | 44 | logger.info('Using %s:%s as SMTP server.', CONFIG.SMTP.HOSTNAME, CONFIG.SMTP.PORT) |
44 | 45 | ||
45 | let tls | 46 | let tls |
@@ -97,12 +98,12 @@ class Emailer { | |||
97 | const channelName = video.VideoChannel.getDisplayName() | 98 | const channelName = video.VideoChannel.getDisplayName() |
98 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() | 99 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() |
99 | 100 | ||
100 | const text = `Hi dear user,\n\n` + | 101 | const text = 'Hi dear user,\n\n' + |
101 | `Your subscription ${channelName} just published a new video: ${video.name}` + | 102 | `Your subscription ${channelName} just published a new video: ${video.name}` + |
102 | `\n\n` + | 103 | '\n\n' + |
103 | `You can view it on ${videoUrl} ` + | 104 | `You can view it on ${videoUrl} ` + |
104 | `\n\n` + | 105 | '\n\n' + |
105 | `Cheers,\n` + | 106 | 'Cheers,\n' + |
106 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | 107 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
107 | 108 | ||
108 | const emailPayload: EmailPayload = { | 109 | const emailPayload: EmailPayload = { |
@@ -118,10 +119,10 @@ class Emailer { | |||
118 | const followerName = actorFollow.ActorFollower.Account.getDisplayName() | 119 | const followerName = actorFollow.ActorFollower.Account.getDisplayName() |
119 | const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName() | 120 | const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName() |
120 | 121 | ||
121 | const text = `Hi dear user,\n\n` + | 122 | const text = 'Hi dear user,\n\n' + |
122 | `Your ${followType} ${followingName} has a new subscriber: ${followerName}` + | 123 | `Your ${followType} ${followingName} has a new subscriber: ${followerName}` + |
123 | `\n\n` + | 124 | '\n\n' + |
124 | `Cheers,\n` + | 125 | 'Cheers,\n' + |
125 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | 126 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
126 | 127 | ||
127 | const emailPayload: EmailPayload = { | 128 | const emailPayload: EmailPayload = { |
@@ -136,10 +137,10 @@ class Emailer { | |||
136 | addNewInstanceFollowerNotification (to: string[], actorFollow: MActorFollowActors) { | 137 | addNewInstanceFollowerNotification (to: string[], actorFollow: MActorFollowActors) { |
137 | const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : '' | 138 | const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : '' |
138 | 139 | ||
139 | const text = `Hi dear admin,\n\n` + | 140 | const text = 'Hi dear admin,\n\n' + |
140 | `Your instance has a new follower: ${actorFollow.ActorFollower.url}${awaitingApproval}` + | 141 | `Your instance has a new follower: ${actorFollow.ActorFollower.url}${awaitingApproval}` + |
141 | `\n\n` + | 142 | '\n\n' + |
142 | `Cheers,\n` + | 143 | 'Cheers,\n' + |
143 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | 144 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
144 | 145 | ||
145 | const emailPayload: EmailPayload = { | 146 | const emailPayload: EmailPayload = { |
@@ -152,10 +153,10 @@ class Emailer { | |||
152 | } | 153 | } |
153 | 154 | ||
154 | addAutoInstanceFollowingNotification (to: string[], actorFollow: MActorFollowActors) { | 155 | addAutoInstanceFollowingNotification (to: string[], actorFollow: MActorFollowActors) { |
155 | const text = `Hi dear admin,\n\n` + | 156 | const text = 'Hi dear admin,\n\n' + |
156 | `Your instance automatically followed a new instance: ${actorFollow.ActorFollowing.url}` + | 157 | `Your instance automatically followed a new instance: ${actorFollow.ActorFollowing.url}` + |
157 | `\n\n` + | 158 | '\n\n' + |
158 | `Cheers,\n` + | 159 | 'Cheers,\n' + |
159 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | 160 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
160 | 161 | ||
161 | const emailPayload: EmailPayload = { | 162 | const emailPayload: EmailPayload = { |
@@ -170,12 +171,12 @@ class Emailer { | |||
170 | myVideoPublishedNotification (to: string[], video: MVideo) { | 171 | myVideoPublishedNotification (to: string[], video: MVideo) { |
171 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() | 172 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() |
172 | 173 | ||
173 | const text = `Hi dear user,\n\n` + | 174 | const text = 'Hi dear user,\n\n' + |
174 | `Your video ${video.name} has been published.` + | 175 | `Your video ${video.name} has been published.` + |
175 | `\n\n` + | 176 | '\n\n' + |
176 | `You can view it on ${videoUrl} ` + | 177 | `You can view it on ${videoUrl} ` + |
177 | `\n\n` + | 178 | '\n\n' + |
178 | `Cheers,\n` + | 179 | 'Cheers,\n' + |
179 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | 180 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
180 | 181 | ||
181 | const emailPayload: EmailPayload = { | 182 | const emailPayload: EmailPayload = { |
@@ -190,12 +191,12 @@ class Emailer { | |||
190 | myVideoImportSuccessNotification (to: string[], videoImport: MVideoImportVideo) { | 191 | myVideoImportSuccessNotification (to: string[], videoImport: MVideoImportVideo) { |
191 | const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath() | 192 | const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath() |
192 | 193 | ||
193 | const text = `Hi dear user,\n\n` + | 194 | const text = 'Hi dear user,\n\n' + |
194 | `Your video import ${videoImport.getTargetIdentifier()} is finished.` + | 195 | `Your video import ${videoImport.getTargetIdentifier()} is finished.` + |
195 | `\n\n` + | 196 | '\n\n' + |
196 | `You can view the imported video on ${videoUrl} ` + | 197 | `You can view the imported video on ${videoUrl} ` + |
197 | `\n\n` + | 198 | '\n\n' + |
198 | `Cheers,\n` + | 199 | 'Cheers,\n' + |
199 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | 200 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
200 | 201 | ||
201 | const emailPayload: EmailPayload = { | 202 | const emailPayload: EmailPayload = { |
@@ -210,12 +211,12 @@ class Emailer { | |||
210 | myVideoImportErrorNotification (to: string[], videoImport: MVideoImport) { | 211 | myVideoImportErrorNotification (to: string[], videoImport: MVideoImport) { |
211 | const importUrl = WEBSERVER.URL + '/my-account/video-imports' | 212 | const importUrl = WEBSERVER.URL + '/my-account/video-imports' |
212 | 213 | ||
213 | const text = `Hi dear user,\n\n` + | 214 | const text = 'Hi dear user,\n\n' + |
214 | `Your video import ${videoImport.getTargetIdentifier()} encountered an error.` + | 215 | `Your video import ${videoImport.getTargetIdentifier()} encountered an error.` + |
215 | `\n\n` + | 216 | '\n\n' + |
216 | `See your videos import dashboard for more information: ${importUrl}` + | 217 | `See your videos import dashboard for more information: ${importUrl}` + |
217 | `\n\n` + | 218 | '\n\n' + |
218 | `Cheers,\n` + | 219 | 'Cheers,\n' + |
219 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | 220 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
220 | 221 | ||
221 | const emailPayload: EmailPayload = { | 222 | const emailPayload: EmailPayload = { |
@@ -232,12 +233,12 @@ class Emailer { | |||
232 | const video = comment.Video | 233 | const video = comment.Video |
233 | const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() | 234 | const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() |
234 | 235 | ||
235 | const text = `Hi dear user,\n\n` + | 236 | const text = 'Hi dear user,\n\n' + |
236 | `A new comment has been posted by ${accountName} on your video ${video.name}` + | 237 | `A new comment has been posted by ${accountName} on your video ${video.name}` + |
237 | `\n\n` + | 238 | '\n\n' + |
238 | `You can view it on ${commentUrl} ` + | 239 | `You can view it on ${commentUrl} ` + |
239 | `\n\n` + | 240 | '\n\n' + |
240 | `Cheers,\n` + | 241 | 'Cheers,\n' + |
241 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | 242 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
242 | 243 | ||
243 | const emailPayload: EmailPayload = { | 244 | const emailPayload: EmailPayload = { |
@@ -254,12 +255,12 @@ class Emailer { | |||
254 | const video = comment.Video | 255 | const video = comment.Video |
255 | const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() | 256 | const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() |
256 | 257 | ||
257 | const text = `Hi dear user,\n\n` + | 258 | const text = 'Hi dear user,\n\n' + |
258 | `${accountName} mentioned you on video ${video.name}` + | 259 | `${accountName} mentioned you on video ${video.name}` + |
259 | `\n\n` + | 260 | '\n\n' + |
260 | `You can view the comment on ${commentUrl} ` + | 261 | `You can view the comment on ${commentUrl} ` + |
261 | `\n\n` + | 262 | '\n\n' + |
262 | `Cheers,\n` + | 263 | 'Cheers,\n' + |
263 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | 264 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
264 | 265 | ||
265 | const emailPayload: EmailPayload = { | 266 | const emailPayload: EmailPayload = { |
@@ -274,9 +275,9 @@ class Emailer { | |||
274 | addVideoAbuseModeratorsNotification (to: string[], videoAbuse: MVideoAbuseVideo) { | 275 | addVideoAbuseModeratorsNotification (to: string[], videoAbuse: MVideoAbuseVideo) { |
275 | const videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath() | 276 | const videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath() |
276 | 277 | ||
277 | const text = `Hi,\n\n` + | 278 | const text = 'Hi,\n\n' + |
278 | `${WEBSERVER.HOST} received an abuse for the following video ${videoUrl}\n\n` + | 279 | `${WEBSERVER.HOST} received an abuse for the following video ${videoUrl}\n\n` + |
279 | `Cheers,\n` + | 280 | 'Cheers,\n' + |
280 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | 281 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
281 | 282 | ||
282 | const emailPayload: EmailPayload = { | 283 | const emailPayload: EmailPayload = { |
@@ -292,14 +293,14 @@ class Emailer { | |||
292 | const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list' | 293 | const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list' |
293 | const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() | 294 | const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() |
294 | 295 | ||
295 | const text = `Hi,\n\n` + | 296 | const text = 'Hi,\n\n' + |
296 | `A recently added video was auto-blacklisted and requires moderator review before publishing.` + | 297 | 'A recently added video was auto-blacklisted and requires moderator review before publishing.' + |
297 | `\n\n` + | 298 | '\n\n' + |
298 | `You can view it and take appropriate action on ${videoUrl}` + | 299 | `You can view it and take appropriate action on ${videoUrl}` + |
299 | `\n\n` + | 300 | '\n\n' + |
300 | `A full list of auto-blacklisted videos can be reviewed here: ${VIDEO_AUTO_BLACKLIST_URL}` + | 301 | `A full list of auto-blacklisted videos can be reviewed here: ${VIDEO_AUTO_BLACKLIST_URL}` + |
301 | `\n\n` + | 302 | '\n\n' + |
302 | `Cheers,\n` + | 303 | 'Cheers,\n' + |
303 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | 304 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
304 | 305 | ||
305 | const emailPayload: EmailPayload = { | 306 | const emailPayload: EmailPayload = { |
@@ -312,9 +313,9 @@ class Emailer { | |||
312 | } | 313 | } |
313 | 314 | ||
314 | addNewUserRegistrationNotification (to: string[], user: MUser) { | 315 | addNewUserRegistrationNotification (to: string[], user: MUser) { |
315 | const text = `Hi,\n\n` + | 316 | const text = 'Hi,\n\n' + |
316 | `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` + | 317 | `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` + |
317 | `Cheers,\n` + | 318 | 'Cheers,\n' + |
318 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | 319 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
319 | 320 | ||
320 | const emailPayload: EmailPayload = { | 321 | const emailPayload: EmailPayload = { |
@@ -367,11 +368,11 @@ class Emailer { | |||
367 | } | 368 | } |
368 | 369 | ||
369 | addPasswordResetEmailJob (to: string, resetPasswordUrl: string) { | 370 | addPasswordResetEmailJob (to: string, resetPasswordUrl: string) { |
370 | const text = `Hi dear user,\n\n` + | 371 | const text = 'Hi dear user,\n\n' + |
371 | `A reset password procedure for your account ${to} has been requested on ${WEBSERVER.HOST} ` + | 372 | `A reset password procedure for your account ${to} has been requested on ${WEBSERVER.HOST} ` + |
372 | `Please follow this link to reset it: ${resetPasswordUrl} (the link will expire within 1 hour)\n\n` + | 373 | `Please follow this link to reset it: ${resetPasswordUrl} (the link will expire within 1 hour)\n\n` + |
373 | `If you are not the person who initiated this request, please ignore this email.\n\n` + | 374 | 'If you are not the person who initiated this request, please ignore this email.\n\n' + |
374 | `Cheers,\n` + | 375 | 'Cheers,\n' + |
375 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | 376 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
376 | 377 | ||
377 | const emailPayload: EmailPayload = { | 378 | const emailPayload: EmailPayload = { |
@@ -383,12 +384,28 @@ class Emailer { | |||
383 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 384 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
384 | } | 385 | } |
385 | 386 | ||
387 | addPasswordCreateEmailJob (username: string, to: string, resetPasswordUrl: string) { | ||
388 | const text = 'Hi,\n\n' + | ||
389 | `Welcome to your ${WEBSERVER.HOST} PeerTube instance. Your username is: ${username}.\n\n` + | ||
390 | `Please set your password by following this link: ${resetPasswordUrl} (this link will expire within seven days).\n\n` + | ||
391 | 'Cheers,\n' + | ||
392 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | ||
393 | |||
394 | const emailPayload: EmailPayload = { | ||
395 | to: [ to ], | ||
396 | subject: CONFIG.EMAIL.SUBJECT.PREFIX + 'New PeerTube account password', | ||
397 | text | ||
398 | } | ||
399 | |||
400 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | ||
401 | } | ||
402 | |||
386 | addVerifyEmailJob (to: string, verifyEmailUrl: string) { | 403 | addVerifyEmailJob (to: string, verifyEmailUrl: string) { |
387 | const text = `Welcome to PeerTube,\n\n` + | 404 | const text = 'Welcome to PeerTube,\n\n' + |
388 | `To start using PeerTube on ${WEBSERVER.HOST} you must verify your email! ` + | 405 | `To start using PeerTube on ${WEBSERVER.HOST} you must verify your email! ` + |
389 | `Please follow this link to verify this email belongs to you: ${verifyEmailUrl}\n\n` + | 406 | `Please follow this link to verify this email belongs to you: ${verifyEmailUrl}\n\n` + |
390 | `If you are not the person who initiated this request, please ignore this email.\n\n` + | 407 | 'If you are not the person who initiated this request, please ignore this email.\n\n' + |
391 | `Cheers,\n` + | 408 | 'Cheers,\n' + |
392 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | 409 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
393 | 410 | ||
394 | const emailPayload: EmailPayload = { | 411 | const emailPayload: EmailPayload = { |
@@ -442,7 +459,7 @@ class Emailer { | |||
442 | } | 459 | } |
443 | 460 | ||
444 | async sendMail (options: EmailPayload) { | 461 | async sendMail (options: EmailPayload) { |
445 | if (!Emailer.isEnabled()) { | 462 | if (!isEmailEnabled()) { |
446 | throw new Error('Cannot send mail because SMTP is not configured.') | 463 | throw new Error('Cannot send mail because SMTP is not configured.') |
447 | } | 464 | } |
448 | 465 | ||