1 import { createTransport, Transporter } from 'nodemailer'
2 import { isTestInstance } from '../helpers/core-utils'
3 import { bunyanLogger, logger } from '../helpers/logger'
4 import { CONFIG } from '../initializers'
5 import { UserModel } from '../models/account/user'
6 import { VideoModel } from '../models/video/video'
7 import { JobQueue } from './job-queue'
8 import { EmailPayload } from './job-queue/handlers/email'
9 import { readFileSync } from 'fs-extra'
10 import { VideoCommentModel } from '../models/video/video-comment'
11 import { VideoAbuseModel } from '../models/video/video-abuse'
12 import { VideoBlacklistModel } from '../models/video/video-blacklist'
16 private static instance: Emailer
17 private initialized = false
18 private transporter: Transporter
19 private enabled = false
21 private constructor () {}
24 // Already initialized
25 if (this.initialized === true) return
26 this.initialized = true
28 if (CONFIG.SMTP.HOSTNAME && CONFIG.SMTP.PORT) {
29 logger.info('Using %s:%s as SMTP server.', CONFIG.SMTP.HOSTNAME, CONFIG.SMTP.PORT)
32 if (CONFIG.SMTP.CA_FILE) {
34 ca: [ readFileSync(CONFIG.SMTP.CA_FILE) ]
39 if (CONFIG.SMTP.USERNAME && CONFIG.SMTP.PASSWORD) {
41 user: CONFIG.SMTP.USERNAME,
42 pass: CONFIG.SMTP.PASSWORD
46 this.transporter = createTransport({
47 host: CONFIG.SMTP.HOSTNAME,
48 port: CONFIG.SMTP.PORT,
49 secure: CONFIG.SMTP.TLS,
50 debug: CONFIG.LOG.LEVEL === 'debug',
51 logger: bunyanLogger as any,
52 ignoreTLS: CONFIG.SMTP.DISABLE_STARTTLS,
59 if (!isTestInstance()) {
60 logger.error('Cannot use SMTP server because of lack of configuration. PeerTube will not be able to send mails!')
69 async checkConnectionOrDie () {
70 if (!this.transporter) return
72 logger.info('Testing SMTP server...')
75 const success = await this.transporter.verify()
76 if (success !== true) this.dieOnConnectionFailure()
78 logger.info('Successfully connected to SMTP server.')
80 this.dieOnConnectionFailure(err)
84 addNewVideoFromSubscriberNotification (to: string[], video: VideoModel) {
85 const channelName = video.VideoChannel.getDisplayName()
86 const videoUrl = CONFIG.WEBSERVER.URL + video.getWatchStaticPath()
88 const text = `Hi dear user,\n\n` +
89 `Your subscription ${channelName} just published a new video: ${video.name}` +
91 `You can view it on ${videoUrl} ` +
96 const emailPayload: EmailPayload = {
98 subject: channelName + ' just published a new video',
102 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
105 addNewCommentOnMyVideoNotification (to: string[], comment: VideoCommentModel) {
106 const accountName = comment.Account.getDisplayName()
107 const video = comment.Video
108 const commentUrl = CONFIG.WEBSERVER.URL + comment.getCommentStaticPath()
110 const text = `Hi dear user,\n\n` +
111 `A new comment has been posted by ${accountName} on your video ${video.name}` +
113 `You can view it on ${commentUrl} ` +
118 const emailPayload: EmailPayload = {
120 subject: 'New comment on your video ' + video.name,
124 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
127 async addVideoAbuseModeratorsNotification (to: string[], videoAbuse: VideoAbuseModel) {
128 const videoUrl = CONFIG.WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath()
130 const text = `Hi,\n\n` +
131 `${CONFIG.WEBSERVER.HOST} received an abuse for the following video ${videoUrl}\n\n` +
135 const emailPayload: EmailPayload = {
137 subject: '[PeerTube] Received a video abuse',
141 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
144 async addVideoBlacklistNotification (to: string[], videoBlacklist: VideoBlacklistModel) {
145 const videoName = videoBlacklist.Video.name
146 const videoUrl = CONFIG.WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath()
148 const reasonString = videoBlacklist.reason ? ` for the following reason: ${videoBlacklist.reason}` : ''
149 const blockedString = `Your video ${videoName} (${videoUrl} on ${CONFIG.WEBSERVER.HOST} has been blacklisted${reasonString}.`
151 const text = 'Hi,\n\n' +
157 const emailPayload: EmailPayload = {
159 subject: `[PeerTube] Video ${videoName} blacklisted`,
163 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
166 async addVideoUnblacklistNotification (to: string[], video: VideoModel) {
167 const videoUrl = CONFIG.WEBSERVER.URL + video.getWatchStaticPath()
169 const text = 'Hi,\n\n' +
170 `Your video ${video.name} (${videoUrl}) on ${CONFIG.WEBSERVER.HOST} has been unblacklisted.` +
175 const emailPayload: EmailPayload = {
177 subject: `[PeerTube] Video ${video.name} unblacklisted`,
181 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
184 addForgetPasswordEmailJob (to: string, resetPasswordUrl: string) {
185 const text = `Hi dear user,\n\n` +
186 `It seems you forgot your password on ${CONFIG.WEBSERVER.HOST}! ` +
187 `Please follow this link to reset it: ${resetPasswordUrl}\n\n` +
188 `If you are not the person who initiated this request, please ignore this email.\n\n` +
192 const emailPayload: EmailPayload = {
194 subject: 'Reset your PeerTube password',
198 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
201 addVerifyEmailJob (to: string, verifyEmailUrl: string) {
202 const text = `Welcome to PeerTube,\n\n` +
203 `To start using PeerTube on ${CONFIG.WEBSERVER.HOST} you must verify your email! ` +
204 `Please follow this link to verify this email belongs to you: ${verifyEmailUrl}\n\n` +
205 `If you are not the person who initiated this request, please ignore this email.\n\n` +
209 const emailPayload: EmailPayload = {
211 subject: 'Verify your PeerTube email',
215 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
218 addUserBlockJob (user: UserModel, blocked: boolean, reason?: string) {
219 const reasonString = reason ? ` for the following reason: ${reason}` : ''
220 const blockedWord = blocked ? 'blocked' : 'unblocked'
221 const blockedString = `Your account ${user.username} on ${CONFIG.WEBSERVER.HOST} has been ${blockedWord}${reasonString}.`
223 const text = 'Hi,\n\n' +
229 const to = user.email
230 const emailPayload: EmailPayload = {
232 subject: '[PeerTube] Account ' + blockedWord,
236 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
239 sendMail (to: string[], subject: string, text: string) {
241 throw new Error('Cannot send mail because SMTP is not configured.')
244 return this.transporter.sendMail({
245 from: CONFIG.SMTP.FROM_ADDRESS,
252 private dieOnConnectionFailure (err?: Error) {
253 logger.error('Failed to connect to SMTP %s:%d.', CONFIG.SMTP.HOSTNAME, CONFIG.SMTP.PORT, { err })
257 static get Instance () {
258 return this.instance || (this.instance = new this())
262 // ---------------------------------------------------------------------------