aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/emailer.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib/emailer.ts')
-rw-r--r--server/lib/emailer.ts94
1 files changed, 73 insertions, 21 deletions
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts
index 99010f473..8c06e9751 100644
--- a/server/lib/emailer.ts
+++ b/server/lib/emailer.ts
@@ -1,7 +1,7 @@
1import { createTransport, Transporter } from 'nodemailer' 1import { createTransport, Transporter } from 'nodemailer'
2import { isTestInstance } from '../helpers/core-utils' 2import { isTestInstance } from '../helpers/core-utils'
3import { bunyanLogger, logger } from '../helpers/logger' 3import { bunyanLogger, logger } from '../helpers/logger'
4import { CONFIG } from '../initializers' 4import { CONFIG } from '../initializers/config'
5import { UserModel } from '../models/account/user' 5import { UserModel } from '../models/account/user'
6import { VideoModel } from '../models/video/video' 6import { VideoModel } from '../models/video/video'
7import { JobQueue } from './job-queue' 7import { JobQueue } from './job-queue'
@@ -12,6 +12,16 @@ import { VideoAbuseModel } from '../models/video/video-abuse'
12import { VideoBlacklistModel } from '../models/video/video-blacklist' 12import { VideoBlacklistModel } from '../models/video/video-blacklist'
13import { VideoImportModel } from '../models/video/video-import' 13import { VideoImportModel } from '../models/video/video-import'
14import { ActorFollowModel } from '../models/activitypub/actor-follow' 14import { ActorFollowModel } from '../models/activitypub/actor-follow'
15import { WEBSERVER } from '../initializers/constants'
16
17type SendEmailOptions = {
18 to: string[]
19 subject: string
20 text: string
21
22 fromDisplayName?: string
23 replyTo?: string
24}
15 25
16class Emailer { 26class Emailer {
17 27
@@ -82,7 +92,7 @@ class Emailer {
82 92
83 addNewVideoFromSubscriberNotification (to: string[], video: VideoModel) { 93 addNewVideoFromSubscriberNotification (to: string[], video: VideoModel) {
84 const channelName = video.VideoChannel.getDisplayName() 94 const channelName = video.VideoChannel.getDisplayName()
85 const videoUrl = CONFIG.WEBSERVER.URL + video.getWatchStaticPath() 95 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
86 96
87 const text = `Hi dear user,\n\n` + 97 const text = `Hi dear user,\n\n` +
88 `Your subscription ${channelName} just published a new video: ${video.name}` + 98 `Your subscription ${channelName} just published a new video: ${video.name}` +
@@ -120,8 +130,26 @@ class Emailer {
120 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 130 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
121 } 131 }
122 132
133 addNewInstanceFollowerNotification (to: string[], actorFollow: ActorFollowModel) {
134 const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : ''
135
136 const text = `Hi dear admin,\n\n` +
137 `Your instance has a new follower: ${actorFollow.ActorFollower.url}${awaitingApproval}` +
138 `\n\n` +
139 `Cheers,\n` +
140 `PeerTube.`
141
142 const emailPayload: EmailPayload = {
143 to,
144 subject: 'New instance follower',
145 text
146 }
147
148 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
149 }
150
123 myVideoPublishedNotification (to: string[], video: VideoModel) { 151 myVideoPublishedNotification (to: string[], video: VideoModel) {
124 const videoUrl = CONFIG.WEBSERVER.URL + video.getWatchStaticPath() 152 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
125 153
126 const text = `Hi dear user,\n\n` + 154 const text = `Hi dear user,\n\n` +
127 `Your video ${video.name} has been published.` + 155 `Your video ${video.name} has been published.` +
@@ -141,7 +169,7 @@ class Emailer {
141 } 169 }
142 170
143 myVideoImportSuccessNotification (to: string[], videoImport: VideoImportModel) { 171 myVideoImportSuccessNotification (to: string[], videoImport: VideoImportModel) {
144 const videoUrl = CONFIG.WEBSERVER.URL + videoImport.Video.getWatchStaticPath() 172 const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath()
145 173
146 const text = `Hi dear user,\n\n` + 174 const text = `Hi dear user,\n\n` +
147 `Your video import ${videoImport.getTargetIdentifier()} is finished.` + 175 `Your video import ${videoImport.getTargetIdentifier()} is finished.` +
@@ -161,7 +189,7 @@ class Emailer {
161 } 189 }
162 190
163 myVideoImportErrorNotification (to: string[], videoImport: VideoImportModel) { 191 myVideoImportErrorNotification (to: string[], videoImport: VideoImportModel) {
164 const importUrl = CONFIG.WEBSERVER.URL + '/my-account/video-imports' 192 const importUrl = WEBSERVER.URL + '/my-account/video-imports'
165 193
166 const text = `Hi dear user,\n\n` + 194 const text = `Hi dear user,\n\n` +
167 `Your video import ${videoImport.getTargetIdentifier()} encountered an error.` + 195 `Your video import ${videoImport.getTargetIdentifier()} encountered an error.` +
@@ -183,7 +211,7 @@ class Emailer {
183 addNewCommentOnMyVideoNotification (to: string[], comment: VideoCommentModel) { 211 addNewCommentOnMyVideoNotification (to: string[], comment: VideoCommentModel) {
184 const accountName = comment.Account.getDisplayName() 212 const accountName = comment.Account.getDisplayName()
185 const video = comment.Video 213 const video = comment.Video
186 const commentUrl = CONFIG.WEBSERVER.URL + comment.getCommentStaticPath() 214 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
187 215
188 const text = `Hi dear user,\n\n` + 216 const text = `Hi dear user,\n\n` +
189 `A new comment has been posted by ${accountName} on your video ${video.name}` + 217 `A new comment has been posted by ${accountName} on your video ${video.name}` +
@@ -205,7 +233,7 @@ class Emailer {
205 addNewCommentMentionNotification (to: string[], comment: VideoCommentModel) { 233 addNewCommentMentionNotification (to: string[], comment: VideoCommentModel) {
206 const accountName = comment.Account.getDisplayName() 234 const accountName = comment.Account.getDisplayName()
207 const video = comment.Video 235 const video = comment.Video
208 const commentUrl = CONFIG.WEBSERVER.URL + comment.getCommentStaticPath() 236 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
209 237
210 const text = `Hi dear user,\n\n` + 238 const text = `Hi dear user,\n\n` +
211 `${accountName} mentioned you on video ${video.name}` + 239 `${accountName} mentioned you on video ${video.name}` +
@@ -225,10 +253,10 @@ class Emailer {
225 } 253 }
226 254
227 addVideoAbuseModeratorsNotification (to: string[], videoAbuse: VideoAbuseModel) { 255 addVideoAbuseModeratorsNotification (to: string[], videoAbuse: VideoAbuseModel) {
228 const videoUrl = CONFIG.WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath() 256 const videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath()
229 257
230 const text = `Hi,\n\n` + 258 const text = `Hi,\n\n` +
231 `${CONFIG.WEBSERVER.HOST} received an abuse for the following video ${videoUrl}\n\n` + 259 `${WEBSERVER.HOST} received an abuse for the following video ${videoUrl}\n\n` +
232 `Cheers,\n` + 260 `Cheers,\n` +
233 `PeerTube.` 261 `PeerTube.`
234 262
@@ -241,15 +269,38 @@ class Emailer {
241 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 269 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
242 } 270 }
243 271
272 addVideoAutoBlacklistModeratorsNotification (to: string[], video: VideoModel) {
273 const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list'
274 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
275
276 const text = `Hi,\n\n` +
277 `A recently added video was auto-blacklisted and requires moderator review before publishing.` +
278 `\n\n` +
279 `You can view it and take appropriate action on ${videoUrl}` +
280 `\n\n` +
281 `A full list of auto-blacklisted videos can be reviewed here: ${VIDEO_AUTO_BLACKLIST_URL}` +
282 `\n\n` +
283 `Cheers,\n` +
284 `PeerTube.`
285
286 const emailPayload: EmailPayload = {
287 to,
288 subject: '[PeerTube] An auto-blacklisted video is awaiting review',
289 text
290 }
291
292 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
293 }
294
244 addNewUserRegistrationNotification (to: string[], user: UserModel) { 295 addNewUserRegistrationNotification (to: string[], user: UserModel) {
245 const text = `Hi,\n\n` + 296 const text = `Hi,\n\n` +
246 `User ${user.username} just registered on ${CONFIG.WEBSERVER.HOST} PeerTube instance.\n\n` + 297 `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` +
247 `Cheers,\n` + 298 `Cheers,\n` +
248 `PeerTube.` 299 `PeerTube.`
249 300
250 const emailPayload: EmailPayload = { 301 const emailPayload: EmailPayload = {
251 to, 302 to,
252 subject: '[PeerTube] New user registration on ' + CONFIG.WEBSERVER.HOST, 303 subject: '[PeerTube] New user registration on ' + WEBSERVER.HOST,
253 text 304 text
254 } 305 }
255 306
@@ -258,10 +309,10 @@ class Emailer {
258 309
259 addVideoBlacklistNotification (to: string[], videoBlacklist: VideoBlacklistModel) { 310 addVideoBlacklistNotification (to: string[], videoBlacklist: VideoBlacklistModel) {
260 const videoName = videoBlacklist.Video.name 311 const videoName = videoBlacklist.Video.name
261 const videoUrl = CONFIG.WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() 312 const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath()
262 313
263 const reasonString = videoBlacklist.reason ? ` for the following reason: ${videoBlacklist.reason}` : '' 314 const reasonString = videoBlacklist.reason ? ` for the following reason: ${videoBlacklist.reason}` : ''
264 const blockedString = `Your video ${videoName} (${videoUrl} on ${CONFIG.WEBSERVER.HOST} has been blacklisted${reasonString}.` 315 const blockedString = `Your video ${videoName} (${videoUrl} on ${WEBSERVER.HOST} has been blacklisted${reasonString}.`
265 316
266 const text = 'Hi,\n\n' + 317 const text = 'Hi,\n\n' +
267 blockedString + 318 blockedString +
@@ -279,10 +330,10 @@ class Emailer {
279 } 330 }
280 331
281 addVideoUnblacklistNotification (to: string[], video: VideoModel) { 332 addVideoUnblacklistNotification (to: string[], video: VideoModel) {
282 const videoUrl = CONFIG.WEBSERVER.URL + video.getWatchStaticPath() 333 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
283 334
284 const text = 'Hi,\n\n' + 335 const text = 'Hi,\n\n' +
285 `Your video ${video.name} (${videoUrl}) on ${CONFIG.WEBSERVER.HOST} has been unblacklisted.` + 336 `Your video ${video.name} (${videoUrl}) on ${WEBSERVER.HOST} has been unblacklisted.` +
286 '\n\n' + 337 '\n\n' +
287 'Cheers,\n' + 338 'Cheers,\n' +
288 `PeerTube.` 339 `PeerTube.`
@@ -296,9 +347,9 @@ class Emailer {
296 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 347 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
297 } 348 }
298 349
299 addForgetPasswordEmailJob (to: string, resetPasswordUrl: string) { 350 addPasswordResetEmailJob (to: string, resetPasswordUrl: string) {
300 const text = `Hi dear user,\n\n` + 351 const text = `Hi dear user,\n\n` +
301 `It seems you forgot your password on ${CONFIG.WEBSERVER.HOST}! ` + 352 `A reset password procedure for your account ${to} has been requested on ${WEBSERVER.HOST} ` +
302 `Please follow this link to reset it: ${resetPasswordUrl}\n\n` + 353 `Please follow this link to reset it: ${resetPasswordUrl}\n\n` +
303 `If you are not the person who initiated this request, please ignore this email.\n\n` + 354 `If you are not the person who initiated this request, please ignore this email.\n\n` +
304 `Cheers,\n` + 355 `Cheers,\n` +
@@ -315,7 +366,7 @@ class Emailer {
315 366
316 addVerifyEmailJob (to: string, verifyEmailUrl: string) { 367 addVerifyEmailJob (to: string, verifyEmailUrl: string) {
317 const text = `Welcome to PeerTube,\n\n` + 368 const text = `Welcome to PeerTube,\n\n` +
318 `To start using PeerTube on ${CONFIG.WEBSERVER.HOST} you must verify your email! ` + 369 `To start using PeerTube on ${WEBSERVER.HOST} you must verify your email! ` +
319 `Please follow this link to verify this email belongs to you: ${verifyEmailUrl}\n\n` + 370 `Please follow this link to verify this email belongs to you: ${verifyEmailUrl}\n\n` +
320 `If you are not the person who initiated this request, please ignore this email.\n\n` + 371 `If you are not the person who initiated this request, please ignore this email.\n\n` +
321 `Cheers,\n` + 372 `Cheers,\n` +
@@ -333,7 +384,7 @@ class Emailer {
333 addUserBlockJob (user: UserModel, blocked: boolean, reason?: string) { 384 addUserBlockJob (user: UserModel, blocked: boolean, reason?: string) {
334 const reasonString = reason ? ` for the following reason: ${reason}` : '' 385 const reasonString = reason ? ` for the following reason: ${reason}` : ''
335 const blockedWord = blocked ? 'blocked' : 'unblocked' 386 const blockedWord = blocked ? 'blocked' : 'unblocked'
336 const blockedString = `Your account ${user.username} on ${CONFIG.WEBSERVER.HOST} has been ${blockedWord}${reasonString}.` 387 const blockedString = `Your account ${user.username} on ${WEBSERVER.HOST} has been ${blockedWord}${reasonString}.`
337 388
338 const text = 'Hi,\n\n' + 389 const text = 'Hi,\n\n' +
339 blockedString + 390 blockedString +
@@ -378,7 +429,7 @@ class Emailer {
378 429
379 const fromDisplayName = options.fromDisplayName 430 const fromDisplayName = options.fromDisplayName
380 ? options.fromDisplayName 431 ? options.fromDisplayName
381 : CONFIG.WEBSERVER.HOST 432 : WEBSERVER.HOST
382 433
383 return this.transporter.sendMail({ 434 return this.transporter.sendMail({
384 from: `"${fromDisplayName}" <${CONFIG.SMTP.FROM_ADDRESS}>`, 435 from: `"${fromDisplayName}" <${CONFIG.SMTP.FROM_ADDRESS}>`,
@@ -402,5 +453,6 @@ class Emailer {
402// --------------------------------------------------------------------------- 453// ---------------------------------------------------------------------------
403 454
404export { 455export {
405 Emailer 456 Emailer,
457 SendEmailOptions
406} 458}