Personal blog written from scratch using Node.js, Bootstrap, and MySQL. https://jrtechs.net
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

205 lines
5.5 KiB

  1. /**
  2. * File which sends emails to me from
  3. * a captcha protected form on the contact
  4. * page.
  5. *
  6. * @author Jeffery Russell 8-19-18
  7. */
  8. /** used for file IO */
  9. const utils = require('../utils/utils.js');
  10. /** used for static files */
  11. const includes = require('../includes/includes');
  12. /** for parsing post data */
  13. const qs = require('querystring');
  14. /** cleans form submission */
  15. const sanitizer = require('sanitizer');
  16. /** used to send post data for the captcha */
  17. const Request = require('request');
  18. /** sends the email using a throw away gmail account */
  19. const nodemailer = require("nodemailer");
  20. /** agent for sending the email */
  21. const smtpTransport = require('nodemailer-smtp-transport');
  22. /** Used to load the config file from the disk */
  23. const config = require('../utils/configLoader').getConfig();
  24. //captcha secret
  25. const CAPTCHA_SECRET = config.CAPTCHA_SECRET;
  26. //password to gmail account
  27. const EMAIL_PASSWORD = config.EMAIL_PASSWORD;
  28. /**
  29. * Verifies if the captcha response recieved from the post data was
  30. * valid, or are bots trying to get around the captcha
  31. *
  32. * @param data captcha data from post request
  33. * @returns {Promise} resolves whether the captcha is valid
  34. */
  35. const verifyCapcha = function(data)
  36. {
  37. const recaptcha_url = "https://www.google.com/recaptcha/api/siteverify?" +
  38. "secret=" + CAPTCHA_SECRET + "&" +
  39. "response=" + data;
  40. return sync = new Promise(function(resolve, reject)
  41. {
  42. Request(recaptcha_url,
  43. function (error, response, body)
  44. {
  45. if (!error && response.statusCode == 200)
  46. {
  47. const googleAnswer = JSON.parse(body);
  48. if(googleAnswer.success == true)
  49. {
  50. resolve(true);
  51. }
  52. else
  53. {
  54. resolve(false);
  55. }
  56. }
  57. else
  58. {
  59. resolve(false);
  60. }
  61. }
  62. );
  63. });
  64. };
  65. /**
  66. * Sends a email to my personal emaail address using a throw away
  67. * gmail account
  68. *
  69. * @param name from contact form
  70. * @param email from contact form
  71. * @param message from contact form
  72. */
  73. const sendEmail = function(name, email, message)
  74. {
  75. const transporter = nodemailer.createTransport(smtpTransport({
  76. service: 'gmail',
  77. host: 'smtp.gmail.com',
  78. auth: {
  79. user: config.GMAIL_ACCOUNT,
  80. pass: EMAIL_PASSWORD
  81. }
  82. }));
  83. const mailOptions =
  84. {
  85. to: config.DESTINATION_EMAIL, // list of receivers
  86. subject: "Jrtechs.net form submission", // Subject line
  87. text: message, // plaintext body
  88. html: message
  89. };
  90. // send mail with defined transport object
  91. transporter.sendMail(mailOptions, function(error, response)
  92. {
  93. if(error)
  94. {
  95. console.log(error);
  96. }
  97. else
  98. {
  99. console.log("Message sent: " + response);
  100. }
  101. transporter.close(); // shut down the connection pool, no more messages
  102. });
  103. };
  104. /**
  105. * If there was post data on the contact page, it processes it to see
  106. * if it was a valid captcha request and sends an email. If no post data was sent,
  107. * the normal form is displayed
  108. *
  109. * @param request -- main express request
  110. * @returns {Promise} renders the html of the contact widget
  111. */
  112. const processContactPage = function(request)
  113. {
  114. return new Promise(function(resolve, reject)
  115. {
  116. utils.getPostData(request).then(function(postData)
  117. {
  118. const data = qs.parse(postData);
  119. if(data.name &&
  120. data.email &&
  121. data["g-recaptcha-response"] &&
  122. data.message)
  123. {
  124. verifyCapcha(sanitizer.sanitize(data["g-recaptcha-response"]))
  125. .then(function(valid)
  126. {
  127. if(valid)
  128. {
  129. resolve(utils.include("includes/html/messageSent.html"));
  130. sendEmail(data.name, data.email, data.message);
  131. }
  132. else
  133. {
  134. resolve(utils.include("includes/html/invalidCaptcha.html"));
  135. }
  136. });
  137. }
  138. else
  139. {
  140. resolve(utils.include("includes/html/contact.html"));
  141. }
  142. }).catch(function(err)
  143. {
  144. reject(err);
  145. })
  146. });
  147. };
  148. module.exports =
  149. {
  150. /**
  151. * Displays the contact page along with the header, sidebar, and footer.
  152. * This uses the admin header because it doesn't need any minified css
  153. * which has been purged of some css classes which are not used in any
  154. * of the blog posts.
  155. *
  156. * @param request -- main express request
  157. * @param result -- renders the html of the contact page
  158. */
  159. main: function(request, result)
  160. {
  161. result.writeHead(200, {'Content-Type': 'text/html'});
  162. Promise.all([includes.printAdminHeader(),
  163. processContactPage(request),
  164. require("../sidebar/sidebar.js").main(),
  165. includes.printFooter()]).then(function(content)
  166. {
  167. result.write(content.join(''));
  168. result.end();
  169. }).catch(function(err)
  170. {
  171. console.log(err);
  172. });
  173. }
  174. };