EmailSender.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. const { callbackify } = require('util')
  2. const logger = require('logger-sharelatex')
  3. const metrics = require('metrics-sharelatex')
  4. const Settings = require('settings-sharelatex')
  5. const nodemailer = require('nodemailer')
  6. const sesTransport = require('nodemailer-ses-transport')
  7. const mandrillTransport = require('nodemailer-mandrill-transport')
  8. const OError = require('@overleaf/o-error')
  9. const RateLimiter = require('../../infrastructure/RateLimiter')
  10. const _ = require('underscore')
  11. const EMAIL_SETTINGS = Settings.email || {}
  12. module.exports = {
  13. sendEmail: callbackify(sendEmail),
  14. promises: {
  15. sendEmail
  16. }
  17. }
  18. const client = getClient()
  19. function getClient() {
  20. let client
  21. if (EMAIL_SETTINGS.parameters) {
  22. const emailParameters = EMAIL_SETTINGS.parameters
  23. if (emailParameters.AWSAccessKeyID || EMAIL_SETTINGS.driver === 'ses') {
  24. logger.log('using aws ses for email')
  25. client = nodemailer.createTransport(sesTransport(emailParameters))
  26. } else if (emailParameters.sendgridApiKey) {
  27. throw new OError({
  28. message:
  29. 'sendgridApiKey configuration option is deprecated, use SMTP instead'
  30. })
  31. } else if (emailParameters.MandrillApiKey) {
  32. logger.log('using mandril for email')
  33. client = nodemailer.createTransport(
  34. mandrillTransport({
  35. auth: {
  36. apiKey: emailParameters.MandrillApiKey
  37. }
  38. })
  39. )
  40. } else {
  41. logger.log('using smtp for email')
  42. const smtp = _.pick(
  43. emailParameters,
  44. 'host',
  45. 'port',
  46. 'secure',
  47. 'auth',
  48. 'ignoreTLS'
  49. )
  50. client = nodemailer.createTransport(smtp)
  51. }
  52. } else {
  53. logger.warn(
  54. 'Email transport and/or parameters not defined. No emails will be sent.'
  55. )
  56. client = {
  57. async sendMail(options) {
  58. logger.log({ options }, 'Would send email if enabled.')
  59. }
  60. }
  61. }
  62. return client
  63. }
  64. async function sendEmail(options) {
  65. try {
  66. const canContinue = await checkCanSendEmail(options)
  67. if (!canContinue) {
  68. logger.log(
  69. {
  70. sendingUser_id: options.sendingUser_id,
  71. to: options.to,
  72. subject: options.subject,
  73. canContinue
  74. },
  75. 'rate limit hit for sending email, not sending'
  76. )
  77. throw new OError({ message: 'rate limit hit sending email' })
  78. }
  79. metrics.inc('email')
  80. let sendMailOptions = {
  81. to: options.to,
  82. from: EMAIL_SETTINGS.fromAddress || '',
  83. subject: options.subject,
  84. html: options.html,
  85. text: options.text,
  86. replyTo: options.replyTo || EMAIL_SETTINGS.replyToAddress,
  87. socketTimeout: 30 * 1000
  88. }
  89. if (EMAIL_SETTINGS.textEncoding != null) {
  90. sendMailOptions.textEncoding = EMAIL_SETTINGS.textEncoding
  91. }
  92. await client.sendMail(sendMailOptions)
  93. } catch (err) {
  94. throw new OError({
  95. message: 'error sending message'
  96. }).withCause(err)
  97. }
  98. }
  99. async function checkCanSendEmail(options) {
  100. if (options.sendingUser_id == null) {
  101. // email not sent from user, not rate limited
  102. return true
  103. }
  104. const opts = {
  105. endpointName: 'send_email',
  106. timeInterval: 60 * 60 * 3,
  107. subjectName: options.sendingUser_id,
  108. throttle: 100
  109. }
  110. const allowed = await RateLimiter.promises.addCount(opts)
  111. return allowed
  112. }