SMAM (short for Send Me A Mail) is a free (as in freedom) contact form embedding software.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. var pug = require('pug');
  2. var nodemailer = require('nodemailer');
  3. var settings = require('./settings');
  4. // Web server
  5. var bodyParser = require('body-parser');
  6. var express = require('express');
  7. var app = express();
  8. // Logging
  9. var printit = require('printit');
  10. var log = printit({
  11. prefix: 'SMAM',
  12. date: true
  13. });
  14. // nodemailer initial configuration
  15. var transporter = nodemailer.createTransport(settings.mailserver);
  16. // Serve static (JS + HTML) files
  17. app.use(express.static('front'));
  18. // Body parsing
  19. app.use(bodyParser.urlencoded({ extended: true }));
  20. app.use(bodyParser.json());
  21. // A request on /send with user input = mail to be sent
  22. app.post('/send', function(req, res, next) {
  23. // Count the failures
  24. let status = {
  25. failed: 0,
  26. total: settings.recipients.length
  27. };
  28. // params will be used as:
  29. // - values for html generation from the pug template
  30. // - parameters for sending the mail(s)
  31. let params = {
  32. subject: req.body.subj,
  33. from: req.body.name + ' <' + req.body.addr + '>',
  34. html: req.body.text
  35. };
  36. // Replacing the mail's content with HTML from the pug template
  37. // Commenting the line below will bypass the generation and only user the
  38. // text entered by the user
  39. params.html = pug.renderFile('template.pug', params);
  40. log.info('Sending message from ' + params.from);
  41. // Send the email to all users
  42. sendMails(params, function(err, infos) {
  43. if(err) {
  44. log.error(err)
  45. }
  46. logStatus(infos);
  47. }, function() {
  48. res.header('Access-Control-Allow-Origin', '*');
  49. if(status.failed === status.total) {
  50. res.status(500).send()
  51. } else {
  52. res.status(200).send();
  53. }
  54. })
  55. });
  56. // Use either the default port or the one chosen by the user (PORT env variable)
  57. var port = process.env.PORT || 1970;
  58. // Start the server
  59. app.listen(port, function() {
  60. log.info('Server started on port ' + port);
  61. });
  62. // Send mails to the recipients specified in the JSON settings file
  63. // content: object containing mail params
  64. // {
  65. // subject: String
  66. // from: String (following RFC 1036 (https://tools.ietf.org/html/rfc1036#section-2.1.1))
  67. // html: String
  68. // }
  69. // update(next, infos): Called each time a mail is sent with the infos provided
  70. // by nodemailer
  71. // done(): Called once each mail has been sent
  72. function sendMails(params, update, done) {
  73. let mails = settings.recipients.map((recipient) => {
  74. // Promise for each recipient to send each mail asynchronously
  75. return new Promise((sent) => {
  76. params.to = recipient;
  77. // Send the email
  78. transporter.sendMail(params, (err, infos) => {
  79. if(err) {
  80. return update(err, recipient);
  81. }
  82. update(null, infos);
  83. // Promise callback
  84. sent();
  85. });
  86. });
  87. });
  88. // Run all the promises (= send all the mails)
  89. Promise.all(mails).then(done);
  90. }
  91. // Produces log from the infos provided by nodemailer
  92. // infos: infos provided by nodemailer
  93. // return: nothing
  94. function logStatus(infos) {
  95. if(infos.accepted.length !== 0) {
  96. log.info('Message sent to ' + infos.accepted[0]);
  97. }
  98. if(infos.rejected.length !== 0) {
  99. status.failed++;
  100. log.info('Message failed to send to ' + infos.rejected[0]);
  101. }
  102. }