emailer.ts 6.9 KB

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