074d4ad44b8c50aebcba69cb3ff45062bc7f430f
[oweals/peertube.git] / server / lib / emailer.ts
1 import { createTransport, Transporter } from 'nodemailer'
2 import { UserRight } from '../../shared/models/users'
3 import { isTestInstance } from '../helpers/core-utils'
4 import { bunyanLogger, logger } from '../helpers/logger'
5 import { CONFIG } from '../initializers'
6 import { UserModel } from '../models/account/user'
7 import { VideoModel } from '../models/video/video'
8 import { JobQueue } from './job-queue'
9 import { EmailPayload } from './job-queue/handlers/email'
10 import { readFileSync } from 'fs-extra'
11
12 class Emailer {
13
14   private static instance: Emailer
15   private initialized = false
16   private transporter: Transporter
17   private enabled = false
18
19   private constructor () {}
20
21   init () {
22     // Already initialized
23     if (this.initialized === true) return
24     this.initialized = true
25
26     if (CONFIG.SMTP.HOSTNAME && CONFIG.SMTP.PORT) {
27       logger.info('Using %s:%s as SMTP server.', CONFIG.SMTP.HOSTNAME, CONFIG.SMTP.PORT)
28
29       let tls
30       if (CONFIG.SMTP.CA_FILE) {
31         tls = {
32           ca: [ readFileSync(CONFIG.SMTP.CA_FILE) ]
33         }
34       }
35
36       let auth
37       if (CONFIG.SMTP.USERNAME && CONFIG.SMTP.PASSWORD) {
38         auth = {
39           user: CONFIG.SMTP.USERNAME,
40           pass: CONFIG.SMTP.PASSWORD
41         }
42       }
43
44       this.transporter = createTransport({
45         host: CONFIG.SMTP.HOSTNAME,
46         port: CONFIG.SMTP.PORT,
47         secure: CONFIG.SMTP.TLS,
48         debug: CONFIG.LOG.LEVEL === 'debug',
49         logger: bunyanLogger as any,
50         ignoreTLS: CONFIG.SMTP.DISABLE_STARTTLS,
51         tls,
52         auth
53       })
54
55       this.enabled = true
56     } else {
57       if (!isTestInstance()) {
58         logger.error('Cannot use SMTP server because of lack of configuration. PeerTube will not be able to send mails!')
59       }
60     }
61   }
62
63   isEnabled () {
64     return this.enabled
65   }
66
67   async checkConnectionOrDie () {
68     if (!this.transporter) return
69
70     logger.info('Testing SMTP server...')
71
72     try {
73       const success = await this.transporter.verify()
74       if (success !== true) this.dieOnConnectionFailure()
75
76       logger.info('Successfully connected to SMTP server.')
77     } catch (err) {
78       this.dieOnConnectionFailure(err)
79     }
80   }
81
82   addForgetPasswordEmailJob (to: string, resetPasswordUrl: string) {
83     const text = `Hi dear user,\n\n` +
84       `It seems you forgot your password on ${CONFIG.WEBSERVER.HOST}! ` +
85       `Please follow this link to reset it: ${resetPasswordUrl}\n\n` +
86       `If you are not the person who initiated this request, please ignore this email.\n\n` +
87       `Cheers,\n` +
88       `PeerTube.`
89
90     const emailPayload: EmailPayload = {
91       to: [ to ],
92       subject: 'Reset your PeerTube password',
93       text
94     }
95
96     return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
97   }
98
99   addVerifyEmailJob (to: string, verifyEmailUrl: string) {
100     const text = `Welcome to PeerTube,\n\n` +
101       `To start using PeerTube on ${CONFIG.WEBSERVER.HOST} you must  verify your email! ` +
102       `Please follow this link to verify this email belongs to you: ${verifyEmailUrl}\n\n` +
103       `If you are not the person who initiated this request, please ignore this email.\n\n` +
104       `Cheers,\n` +
105       `PeerTube.`
106
107     const emailPayload: EmailPayload = {
108       to: [ to ],
109       subject: 'Verify your PeerTube email',
110       text
111     }
112
113     return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
114   }
115
116   async addVideoAbuseReportJob (videoId: number) {
117     const video = await VideoModel.load(videoId)
118     if (!video) throw new Error('Unknown Video id during Abuse report.')
119
120     const text = `Hi,\n\n` +
121       `Your instance received an abuse for the following video ${video.url}\n\n` +
122       `Cheers,\n` +
123       `PeerTube.`
124
125     const to = await UserModel.listEmailsWithRight(UserRight.MANAGE_VIDEO_ABUSES)
126     const emailPayload: EmailPayload = {
127       to,
128       subject: '[PeerTube] Received a video abuse',
129       text
130     }
131
132     return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
133   }
134
135   async addVideoBlacklistReportJob (videoId: number, reason?: string) {
136     const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoId)
137     if (!video) throw new Error('Unknown Video id during Blacklist report.')
138     // It's not our user
139     if (video.remote === true) return
140
141     const user = await UserModel.loadById(video.VideoChannel.Account.userId)
142
143     const reasonString = reason ? ` for the following reason: ${reason}` : ''
144     const blockedString = `Your video ${video.name} on ${CONFIG.WEBSERVER.HOST} has been blacklisted${reasonString}.`
145
146     const text = 'Hi,\n\n' +
147       blockedString +
148       '\n\n' +
149       'Cheers,\n' +
150       `PeerTube.`
151
152     const to = user.email
153     const emailPayload: EmailPayload = {
154       to: [ to ],
155       subject: `[PeerTube] Video ${video.name} blacklisted`,
156       text
157     }
158
159     return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
160   }
161
162   async addVideoUnblacklistReportJob (videoId: number) {
163     const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoId)
164     if (!video) throw new Error('Unknown Video id during Blacklist report.')
165     // It's not our user
166     if (video.remote === true) return
167
168     const user = await UserModel.loadById(video.VideoChannel.Account.userId)
169
170     const text = 'Hi,\n\n' +
171       `Your video ${video.name} on ${CONFIG.WEBSERVER.HOST} has been unblacklisted.` +
172       '\n\n' +
173       'Cheers,\n' +
174       `PeerTube.`
175
176     const to = user.email
177     const emailPayload: EmailPayload = {
178       to: [ to ],
179       subject: `[PeerTube] Video ${video.name} unblacklisted`,
180       text
181     }
182
183     return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
184   }
185
186   addUserBlockJob (user: UserModel, blocked: boolean, reason?: string) {
187     const reasonString = reason ? ` for the following reason: ${reason}` : ''
188     const blockedWord = blocked ? 'blocked' : 'unblocked'
189     const blockedString = `Your account ${user.username} on ${CONFIG.WEBSERVER.HOST} has been ${blockedWord}${reasonString}.`
190
191     const text = 'Hi,\n\n' +
192       blockedString +
193       '\n\n' +
194       'Cheers,\n' +
195       `PeerTube.`
196
197     const to = user.email
198     const emailPayload: EmailPayload = {
199       to: [ to ],
200       subject: '[PeerTube] Account ' + blockedWord,
201       text
202     }
203
204     return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
205   }
206
207   sendMail (to: string[], subject: string, text: string) {
208     if (!this.transporter) {
209       throw new Error('Cannot send mail because SMTP is not configured.')
210     }
211
212     return this.transporter.sendMail({
213       from: CONFIG.SMTP.FROM_ADDRESS,
214       to: to.join(','),
215       subject,
216       text
217     })
218   }
219
220   private dieOnConnectionFailure (err?: Error) {
221     logger.error('Failed to connect to SMTP %s:%d.', CONFIG.SMTP.HOSTNAME, CONFIG.SMTP.PORT, { err })
222     process.exit(-1)
223   }
224
225   static get Instance () {
226     return this.instance || (this.instance = new this())
227   }
228 }
229
230 // ---------------------------------------------------------------------------
231
232 export {
233   Emailer
234 }