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.

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